Merge branch 'master' into mar-kolya/profiling-lz4

This commit is contained in:
Nikolay Martynov 2020-03-02 10:02:09 -05:00
commit b3d81281f6
224 changed files with 2791 additions and 1865 deletions

3
.github/CODEOWNERS vendored
View File

@ -2,3 +2,6 @@
# https://help.github.com/en/articles/about-code-owners # https://help.github.com/en/articles/about-code-owners
* @DataDog/apm-java * @DataDog/apm-java
dd-java-agent/agent-profiling/ @DataDog/profiling
dd-smoke-tests/profiling-integration-tests/ @DataDog/profiling

View File

@ -1,4 +1,4 @@
### Contributing ## Contributing
Pull requests for bug fixes are welcome, but before submitting new features or changes to current functionality [open an issue](https://github.com/DataDog/dd-trace-java/issues/new) Pull requests for bug fixes are welcome, but before submitting new features or changes to current functionality [open an issue](https://github.com/DataDog/dd-trace-java/issues/new)
and discuss your ideas or propose the changes you wish to make. After a resolution is reached a PR can be submitted for review. and discuss your ideas or propose the changes you wish to make. After a resolution is reached a PR can be submitted for review.
@ -15,7 +15,14 @@ Java files must be formatted using [google-java-format](https://github.com/googl
Other source files (Groovy, Scala, etc) should ideally be formatted by Intellij Idea's default formatting, but are not enforced. Other source files (Groovy, Scala, etc) should ideally be formatted by Intellij Idea's default formatting, but are not enforced.
### Intellij Idea ### Intellij IDEA
Compiler settings:
* OpenJDK 11 must be installed to build the entire project. Under `SDKs` it must have the name `11`.
* Under `Build, Execution, Deployment > Compiler > Java Compiler` disable `Use '--release' option for cross-compilation`
Required plugins:
* [Lombok](https://plugins.jetbrains.com/plugin/6317-lombok-plugin)
Suggested plugins and settings: Suggested plugins and settings:
@ -25,6 +32,5 @@ Suggested plugins and settings:
* With java use the following import layout (groovy should still use the default) to ensure consistency with google-java-format: * With java use the following import layout (groovy should still use the default) to ensure consistency with google-java-format:
![import layout](https://user-images.githubusercontent.com/734411/43430811-28442636-94ae-11e8-86f1-f270ddcba023.png) ![import layout](https://user-images.githubusercontent.com/734411/43430811-28442636-94ae-11e8-86f1-f270ddcba023.png)
* [Google Java Format](https://plugins.jetbrains.com/plugin/8527-google-java-format) * [Google Java Format](https://plugins.jetbrains.com/plugin/8527-google-java-format)
* [Lombok](https://plugins.jetbrains.com/plugin/6317-lombok-plugin)
* [Save Actions](https://plugins.jetbrains.com/plugin/7642-save-actions) * [Save Actions](https://plugins.jetbrains.com/plugin/7642-save-actions)
![Recommended Settings](https://user-images.githubusercontent.com/734411/43430944-db84bf8a-94ae-11e8-8cec-0daa064937c4.png) ![Recommended Settings](https://user-images.githubusercontent.com/734411/43430944-db84bf8a-94ae-11e8-8cec-0daa064937c4.png)

View File

@ -23,8 +23,6 @@ public abstract class HttpServerDecorator<REQUEST, CONNECTION, RESPONSE> extends
protected abstract URI url(REQUEST request) throws URISyntaxException; protected abstract URI url(REQUEST request) throws URISyntaxException;
protected abstract String peerHostname(CONNECTION connection);
protected abstract String peerHostIP(CONNECTION connection); protected abstract String peerHostIP(CONNECTION connection);
protected abstract Integer peerPort(CONNECTION connection); protected abstract Integer peerPort(CONNECTION connection);
@ -87,7 +85,6 @@ public abstract class HttpServerDecorator<REQUEST, CONNECTION, RESPONSE> extends
public AgentSpan onConnection(final AgentSpan span, final CONNECTION connection) { public AgentSpan onConnection(final AgentSpan span, final CONNECTION connection) {
assert span != null; assert span != null;
if (connection != null) { if (connection != null) {
span.setTag(Tags.PEER_HOSTNAME.getKey(), peerHostname(connection));
final String ip = peerHostIP(connection); final String ip = peerHostIP(connection);
if (ip != null) { if (ip != null) {
if (VALID_IPV4_ADDRESS.matcher(ip).matches()) { if (VALID_IPV4_ADDRESS.matcher(ip).matches()) {

View File

@ -1,15 +1,13 @@
package datadog.trace.agent.tooling; package datadog.trace.agent.tooling;
import static datadog.trace.agent.tooling.ClassLoaderMatcher.skipClassLoader; import static datadog.trace.agent.tooling.ClassLoaderMatcher.skipClassLoader;
import static datadog.trace.agent.tooling.bytebuddy.GlobalIgnoresMatcher.globalIgnoresMatcher;
import static net.bytebuddy.matcher.ElementMatchers.any; import static net.bytebuddy.matcher.ElementMatchers.any;
import static net.bytebuddy.matcher.ElementMatchers.isAnnotatedWith;
import static net.bytebuddy.matcher.ElementMatchers.nameContains;
import static net.bytebuddy.matcher.ElementMatchers.nameMatches;
import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith; 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 static net.bytebuddy.matcher.ElementMatchers.not;
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;
@ -21,10 +19,10 @@ import java.util.ServiceLoader;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import net.bytebuddy.agent.builder.AgentBuilder; import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.agent.builder.ResettableClassFileTransformer; import net.bytebuddy.agent.builder.ResettableClassFileTransformer;
import net.bytebuddy.description.type.TypeDefinition;
import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.DynamicType; import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.matcher.ElementMatcher; import net.bytebuddy.matcher.ElementMatcher;
import net.bytebuddy.matcher.ElementMatchers;
import net.bytebuddy.utility.JavaModule; import net.bytebuddy.utility.JavaModule;
@Slf4j @Slf4j
@ -54,91 +52,33 @@ public class AgentInstaller {
final Instrumentation inst, final AgentBuilder.Listener... listeners) { final Instrumentation inst, final AgentBuilder.Listener... listeners) {
INSTRUMENTATION = inst; INSTRUMENTATION = inst;
addByteBuddyRawSetting();
FieldBackedProvider.resetContextMatchers();
AgentBuilder agentBuilder = AgentBuilder agentBuilder =
new AgentBuilder.Default() new AgentBuilder.Default()
.disableClassFormatChanges() .disableClassFormatChanges()
.with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION) .with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
.with(new RedefinitionLoggingListener())
.with(AgentBuilder.DescriptionStrategy.Default.POOL_ONLY) .with(AgentBuilder.DescriptionStrategy.Default.POOL_ONLY)
.with(AgentTooling.poolStrategy()) .with(AgentTooling.poolStrategy())
.with(new TransformLoggingListener())
.with(new ClassLoadListener()) .with(new ClassLoadListener())
.with(AgentTooling.locationStrategy()) .with(AgentTooling.locationStrategy())
// FIXME: we cannot enable it yet due to BB/JVM bug, see // FIXME: we cannot enable it yet due to BB/JVM bug, see
// https://github.com/raphw/byte-buddy/issues/558 // https://github.com/raphw/byte-buddy/issues/558
// .with(AgentBuilder.LambdaInstrumentationStrategy.ENABLED) // .with(AgentBuilder.LambdaInstrumentationStrategy.ENABLED)
.ignore(any(), skipClassLoader()) .ignore(any(), skipClassLoader())
// Unlikely to ever need to instrument an annotation: .or(globalIgnoresMatcher())
.or(ElementMatchers.<TypeDescription>isAnnotation())
// Unlikely to ever need to instrument an enum:
.or(ElementMatchers.<TypeDescription>isEnum())
.or(
nameStartsWith("datadog.trace.")
// FIXME: We should remove this once
// https://github.com/raphw/byte-buddy/issues/558 is fixed
.and(
not(
named(
"datadog.trace.bootstrap.instrumentation.java.concurrent.RunnableWrapper")
.or(
named(
"datadog.trace.bootstrap.instrumentation.java.concurrent.CallableWrapper")))))
.or(nameStartsWith("datadog.opentracing."))
.or(nameStartsWith("datadog.slf4j."))
.or(nameStartsWith("net.bytebuddy."))
.or(
nameStartsWith("java.")
.and(
not(
named("java.net.URL")
.or(named("java.net.HttpURLConnection"))
.or(nameStartsWith("java.rmi."))
.or(nameStartsWith("java.util.concurrent."))
.or(
nameStartsWith("java.util.logging.")
// Concurrent instrumentation modifies the strucutre of
// Cleaner class incompaibly with java9+ modules.
// Working around until a long-term fix for modules can be
// put in place.
.and(not(named("java.util.logging.LogManager$Cleaner")))))))
.or(
nameStartsWith("com.sun.")
.and(
not(
nameStartsWith("com.sun.messaging.")
.or(nameStartsWith("com.sun.jersey.api.client")))))
.or(
nameStartsWith("sun.")
.and(
not(
nameStartsWith("sun.net.www.protocol.")
.or(nameStartsWith("sun.rmi.server"))
.or(nameStartsWith("sun.rmi.transport"))
.or(named("sun.net.www.http.HttpClient")))))
.or(nameStartsWith("jdk."))
.or(nameStartsWith("org.aspectj."))
.or(nameStartsWith("org.groovy."))
.or(nameStartsWith("org.codehaus.groovy.macro."))
.or(nameStartsWith("com.intellij.rt.debugger."))
.or(nameStartsWith("com.p6spy."))
.or(nameStartsWith("com.newrelic."))
.or(nameStartsWith("com.dynatrace."))
.or(nameStartsWith("com.jloadtrace."))
.or(nameStartsWith("com.appdynamics."))
.or(nameStartsWith("com.singularity."))
.or(nameStartsWith("com.jinspired."))
.or(nameStartsWith("org.jinspired."))
.or(nameStartsWith("org.apache.log4j.").and(not(named("org.apache.log4j.MDC"))))
.or(nameStartsWith("org.slf4j.").and(not(named("org.slf4j.MDC"))))
.or(nameContains("$JaxbAccessor"))
.or(nameContains("CGLIB$$"))
.or(nameContains("javassist"))
.or(nameContains(".asm."))
.or(nameContains("$__sisu"))
.or(nameMatches("com\\.mchange\\.v2\\.c3p0\\..*Proxy"))
.or(isAnnotatedWith(named("javax.decorator.Decorator")))
.or(matchesConfiguredExcludes()); .or(matchesConfiguredExcludes());
if (log.isDebugEnabled()) {
agentBuilder =
agentBuilder
.with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
.with(new RedefinitionLoggingListener())
.with(new TransformLoggingListener());
}
for (final AgentBuilder.Listener listener : listeners) { for (final AgentBuilder.Listener listener : listeners) {
agentBuilder = agentBuilder.with(listener); agentBuilder = agentBuilder.with(listener);
} }
@ -159,6 +99,23 @@ public class AgentInstaller {
return agentBuilder.installOn(inst); return agentBuilder.installOn(inst);
} }
private static void addByteBuddyRawSetting() {
final String savedPropertyValue = System.getProperty(TypeDefinition.RAW_TYPES_PROPERTY);
try {
System.setProperty(TypeDefinition.RAW_TYPES_PROPERTY, "true");
final boolean rawTypes = TypeDescription.AbstractBase.RAW_TYPES;
if (!rawTypes) {
log.debug("Too late to enable {}", TypeDefinition.RAW_TYPES_PROPERTY);
}
} finally {
if (savedPropertyValue == null) {
System.clearProperty(TypeDefinition.RAW_TYPES_PROPERTY);
} else {
System.setProperty(TypeDefinition.RAW_TYPES_PROPERTY, savedPropertyValue);
}
}
}
private static ElementMatcher.Junction<Object> matchesConfiguredExcludes() { private static ElementMatcher.Junction<Object> matchesConfiguredExcludes() {
final List<String> excludedClasses = Config.get().getExcludedClasses(); final List<String> excludedClasses = Config.get().getExcludedClasses();
ElementMatcher.Junction matcher = none(); ElementMatcher.Junction matcher = none();

View File

@ -1,5 +1,7 @@
package datadog.trace.agent.tooling; package datadog.trace.agent.tooling;
import datadog.trace.agent.tooling.bytebuddy.DDCachingPoolStrategy;
import datadog.trace.agent.tooling.bytebuddy.DDLocationStrategy;
import datadog.trace.bootstrap.WeakMap; import datadog.trace.bootstrap.WeakMap;
/** /**

View File

@ -1,382 +0,0 @@
package datadog.trace.agent.tooling;
import static net.bytebuddy.matcher.ElementMatchers.hasSignature;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import lombok.extern.slf4j.Slf4j;
import net.bytebuddy.build.HashCodeAndEqualsPlugin;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.type.TypeDefinition;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.description.type.TypeList;
import net.bytebuddy.matcher.ElementMatcher;
import net.bytebuddy.matcher.ElementMatchers;
/**
* This class provides some custom ByteBuddy element matchers to use when applying instrumentation
*/
@Slf4j
public class ByteBuddyElementMatchers {
/**
* Matches any type description that declares a super type that matches the provided matcher.
* Exceptions during matching process are logged and ignored.
*
* @param matcher The type to be checked for being a super type of the matched type.
* @param <T> The type of the matched object.
* @return A matcher that matches any type description that declares a super type that matches the
* provided matcher.
* @see ElementMatchers#hasSuperType(net.bytebuddy.matcher.ElementMatcher)
*/
public static <T extends TypeDescription> ElementMatcher.Junction<T> safeHasSuperType(
final ElementMatcher<? super TypeDescription> matcher) {
return safeHasGenericSuperType(new SafeErasureMatcher(matcher));
}
/**
* Matches any type description that declares a super type that matches the provided matcher.
* Exceptions during matching process are logged and ignored.
*
* @param matcher The type to be checked for being a super type of the matched type.
* @param <T> The type of the matched object.
* @return A matcher that matches any type description that declares a super type that matches the
* provided matcher.
* @see ElementMatchers#hasGenericSuperType(net.bytebuddy.matcher.ElementMatcher)
*/
public static <T extends TypeDescription> ElementMatcher.Junction<T> safeHasGenericSuperType(
final ElementMatcher<? super TypeDescription.Generic> matcher) {
return new SafeHasSuperTypeMatcher<>(matcher);
}
/**
* Wraps another matcher to assure that an element is not matched in case that the matching causes
* an {@link Exception}. Logs exception if it happens.
*
* @param matcher The element matcher that potentially throws an exception.
* @param <T> The type of the matched object.
* @return A matcher that returns {@code false} in case that the given matcher throws an
* exception.
*/
public static <T> ElementMatcher.Junction<T> failSafe(
final ElementMatcher<? super T> matcher, final String description) {
return new SafeMatcher<>(matcher, false, description);
}
private static TypeDescription safeAsErasure(final TypeDefinition typeDefinition) {
try {
return typeDefinition.asErasure();
} catch (final Exception e) {
log.debug(
"{} trying to get erasure for target {}: {}",
e.getClass().getSimpleName(),
safeTypeDefinitionName(typeDefinition),
e.getMessage());
return null;
}
}
/**
* An element matcher that matches a super type. This is different from {@link
* net.bytebuddy.matcher.HasSuperTypeMatcher} in the following way:
*
* <ul>
* <li>Exceptions are logged
* <li>When exception happens the rest of the inheritance subtree is discarded (since ByteBuddy
* cannot load/parse type information for it) but search in other subtrees continues
* </ul>
*
* <p>This is useful because this allows us to see when matcher's check is not complete (i.e. part
* of it fails), at the same time it makes best effort instead of failing quickly (like {@code
* failSafe(hasSuperType(...))} does) which means the code is more resilient to classpath
* inconsistencies
*
* @param <T> The type of the matched entity.
* @see net.bytebuddy.matcher.HasSuperTypeMatcher
*/
@HashCodeAndEqualsPlugin.Enhance
public static class SafeHasSuperTypeMatcher<T extends TypeDescription>
extends ElementMatcher.Junction.AbstractBase<T> {
/** The matcher to apply to any super type of the matched type. */
private final ElementMatcher<? super TypeDescription.Generic> matcher;
/**
* Creates a new matcher for a super type.
*
* @param matcher The matcher to apply to any super type of the matched type.
*/
public SafeHasSuperTypeMatcher(final ElementMatcher<? super TypeDescription.Generic> matcher) {
this.matcher = matcher;
}
@Override
public boolean matches(final T target) {
final Set<TypeDescription> checkedInterfaces = new HashSet<>();
// We do not use foreach loop and iterator interface here because we need to catch exceptions
// in {@code getSuperClass} calls
TypeDefinition typeDefinition = target;
while (typeDefinition != null) {
if (matcher.matches(typeDefinition.asGenericType())
|| hasInterface(typeDefinition, checkedInterfaces)) {
return true;
}
typeDefinition = safeGetSuperClass(typeDefinition);
}
return false;
}
private TypeDefinition safeGetSuperClass(final TypeDefinition typeDefinition) {
try {
return typeDefinition.getSuperClass();
} catch (final Exception e) {
log.debug(
"{} trying to get super class for target {}: {}",
e.getClass().getSimpleName(),
safeTypeDefinitionName(typeDefinition),
e.getMessage());
return null;
}
}
/**
* Matches a type's interfaces against the provided matcher.
*
* @param typeDefinition The type for which to check all implemented interfaces.
* @param checkedInterfaces The interfaces that have already been checked.
* @return {@code true} if any interface matches the supplied matcher.
*/
private boolean hasInterface(
final TypeDefinition typeDefinition, final Set<TypeDescription> checkedInterfaces) {
for (final TypeDefinition interfaceType : safeGetInterfaces(typeDefinition)) {
final TypeDescription erasure = safeAsErasure(interfaceType);
if (erasure != null) {
if (checkedInterfaces.add(interfaceType.asErasure())
&& (matcher.matches(interfaceType.asGenericType())
|| hasInterface(interfaceType, checkedInterfaces))) {
return true;
}
}
}
return false;
}
/**
* TypeDefinition#getInterfaces() produces an interator which may throw an exception during
* iteration if an interface is absent from the classpath.
*
* <p>This method exists to allow getting interfaces even if the lookup on one fails.
*/
private List<TypeDefinition> safeGetInterfaces(final TypeDefinition typeDefinition) {
final List<TypeDefinition> interfaceTypes = new ArrayList<>();
try {
final Iterator<TypeDescription.Generic> interfaceIter =
typeDefinition.getInterfaces().iterator();
while (interfaceIter.hasNext()) {
interfaceTypes.add(interfaceIter.next());
}
} catch (final Exception e) {
log.debug(
"{} trying to get interfaces for target {}: {}",
e.getClass().getSimpleName(),
safeTypeDefinitionName(typeDefinition),
e.getMessage());
}
return interfaceTypes;
}
@Override
public String toString() {
return "safeHasSuperType(" + matcher + ")";
}
}
/**
* An element matcher that matches its argument's {@link TypeDescription.Generic} raw type against
* the given matcher for a {@link TypeDescription}. As a wildcard does not define an erasure, a
* runtime exception is thrown when this matcher is applied to a wildcard.
*
* <p>Catches and logs exception if it was thrown when getting erasure, returning false.
*
* @param <T> The type of the matched entity.
* @see net.bytebuddy.matcher.ErasureMatcher
*/
@HashCodeAndEqualsPlugin.Enhance
public static class SafeErasureMatcher<T extends TypeDefinition>
extends ElementMatcher.Junction.AbstractBase<T> {
/** The matcher to apply to the raw type of the matched element. */
private final ElementMatcher<? super TypeDescription> matcher;
/**
* Creates a new erasure matcher.
*
* @param matcher The matcher to apply to the raw type.
*/
public SafeErasureMatcher(final ElementMatcher<? super TypeDescription> matcher) {
this.matcher = matcher;
}
@Override
public boolean matches(final T target) {
final TypeDescription erasure = safeAsErasure(target);
if (erasure == null) {
return false;
} else {
// We would like matcher exceptions to propagate
return matcher.matches(erasure);
}
}
@Override
public String toString() {
return "safeErasure(" + matcher + ")";
}
}
/**
* A fail-safe matcher catches exceptions that are thrown by a delegate matcher and returns an
* alternative value.
*
* <p>Logs exception if it was thrown.
*
* @param <T> The type of the matched entity.
* @see net.bytebuddy.matcher.FailSafeMatcher
*/
@HashCodeAndEqualsPlugin.Enhance
public static class SafeMatcher<T> extends ElementMatcher.Junction.AbstractBase<T> {
/** The delegate matcher that might throw an exception. */
private final ElementMatcher<? super T> matcher;
/** The fallback value in case of an exception. */
private final boolean fallback;
/** The text description to log if exception happens. */
private final String description;
/**
* Creates a new fail-safe element matcher.
*
* @param matcher The delegate matcher that might throw an exception.
* @param fallback The fallback value in case of an exception.
* @param description Descriptive string to log along with exception.
*/
public SafeMatcher(
final ElementMatcher<? super T> matcher, final boolean fallback, final String description) {
this.matcher = matcher;
this.fallback = fallback;
this.description = description;
}
@Override
public boolean matches(final T target) {
try {
return matcher.matches(target);
} catch (final Exception e) {
log.debug(description, e);
return fallback;
}
}
@Override
public String toString() {
return "safeMatcher(try(" + matcher + ") or " + fallback + ")";
}
}
private static String safeTypeDefinitionName(final TypeDefinition td) {
try {
return td.getTypeName();
} catch (final IllegalStateException ex) {
final String message = ex.getMessage();
if (message.startsWith("Cannot resolve type description for ")) {
return message.replace("Cannot resolve type description for ", "");
} else {
return "?";
}
}
}
// TODO: add javadoc
public static <T extends MethodDescription> ElementMatcher.Junction<T> hasSuperMethod(
final ElementMatcher<? super MethodDescription> matcher) {
return new HasSuperMethodMatcher<>(matcher);
}
// TODO: add javadoc
@HashCodeAndEqualsPlugin.Enhance
public static class HasSuperMethodMatcher<T extends MethodDescription>
extends ElementMatcher.Junction.AbstractBase<T> {
private final ElementMatcher<? super MethodDescription> matcher;
public HasSuperMethodMatcher(final ElementMatcher<? super MethodDescription> matcher) {
this.matcher = matcher;
}
@Override
public boolean matches(final MethodDescription target) {
if (target.isConstructor()) {
return false;
}
final Junction<MethodDescription> signatureMatcher = hasSignature(target.asSignatureToken());
TypeDefinition declaringType = target.getDeclaringType();
final Set<TypeDefinition> checkedInterfaces = new HashSet<>();
while (declaringType != null) {
for (final MethodDescription methodDescription : declaringType.getDeclaredMethods()) {
if (signatureMatcher.matches(methodDescription) && matcher.matches(methodDescription)) {
return true;
}
}
if (matchesInterface(declaringType.getInterfaces(), signatureMatcher, checkedInterfaces)) {
return true;
}
declaringType = safeGetSuperClass(declaringType);
}
return false;
}
private boolean matchesInterface(
final TypeList.Generic interfaces,
final Junction<MethodDescription> signatureMatcher,
final Set<TypeDefinition> checkedInterfaces) {
for (final TypeDefinition type : interfaces) {
if (!checkedInterfaces.contains(type)) {
checkedInterfaces.add(type);
for (final MethodDescription methodDescription : type.getDeclaredMethods()) {
if (signatureMatcher.matches(methodDescription) && matcher.matches(methodDescription)) {
return true;
}
}
if (matchesInterface(type.getInterfaces(), signatureMatcher, checkedInterfaces)) {
return true;
}
}
}
return false;
}
private TypeDefinition safeGetSuperClass(final TypeDefinition typeDefinition) {
try {
return typeDefinition.getSuperClass();
} catch (final Exception e) {
log.debug(
"{} trying to get super class for target {}: {}",
e.getClass().getSimpleName(),
safeTypeDefinitionName(typeDefinition),
e.getMessage());
return null;
}
}
@Override
public String toString() {
return "hasSuperMethodMatcher(" + matcher + ")";
}
}
}

View File

@ -1,20 +1,17 @@
package datadog.trace.agent.tooling; package datadog.trace.agent.tooling;
import static datadog.trace.bootstrap.WeakMap.Provider.newWeakMap; import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import datadog.trace.bootstrap.DatadogClassLoader;
import datadog.trace.bootstrap.PatchLogger; import datadog.trace.bootstrap.PatchLogger;
import datadog.trace.bootstrap.WeakMap;
import io.opentracing.util.GlobalTracer; import io.opentracing.util.GlobalTracer;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import net.bytebuddy.matcher.ElementMatcher; import net.bytebuddy.matcher.ElementMatcher;
@Slf4j @Slf4j
public class ClassLoaderMatcher { public final class ClassLoaderMatcher {
public static final ClassLoader BOOTSTRAP_CLASSLOADER = null; public static final ClassLoader BOOTSTRAP_CLASSLOADER = null;
public static final int CACHE_CONCURRENCY =
Math.max(8, Runtime.getRuntime().availableProcessors());
/** A private constructor that must not be invoked. */ /** A private constructor that must not be invoked. */
private ClassLoaderMatcher() { private ClassLoaderMatcher() {
@ -25,58 +22,49 @@ public class ClassLoaderMatcher {
return SkipClassLoaderMatcher.INSTANCE; return SkipClassLoaderMatcher.INSTANCE;
} }
public static ElementMatcher.Junction.AbstractBase<ClassLoader> classLoaderHasClasses( public static ElementMatcher.Junction.AbstractBase<ClassLoader> classLoaderHasNoResources(
final String... names) { final String... resources) {
return new ClassLoaderHasClassMatcher(names); return new ClassLoaderHasNoResourceMatcher(resources);
} }
private static class SkipClassLoaderMatcher private static final class SkipClassLoaderMatcher
extends ElementMatcher.Junction.AbstractBase<ClassLoader> extends ElementMatcher.Junction.AbstractBase<ClassLoader> {
implements WeakMap.ValueSupplier<ClassLoader, Boolean> {
public static final SkipClassLoaderMatcher INSTANCE = new SkipClassLoaderMatcher(); public static final SkipClassLoaderMatcher INSTANCE = new SkipClassLoaderMatcher();
/* Cache of classloader-instance -> (true|false). True = skip instrumentation. False = safe to instrument. */ /* Cache of classloader-instance -> (true|false). True = skip instrumentation. False = safe to instrument. */
private static final WeakMap<ClassLoader, Boolean> SKIP_CACHE = newWeakMap(); private static final Cache<ClassLoader, Boolean> skipCache =
private static final Set<String> CLASSLOADER_CLASSES_TO_SKIP; CacheBuilder.newBuilder().weakKeys().concurrencyLevel(CACHE_CONCURRENCY).build();
private static final String DATADOG_CLASSLOADER_NAME =
static { "datadog.trace.bootstrap.DatadogClassLoader";
final Set<String> classesToSkip = new HashSet<>();
classesToSkip.add("org.codehaus.groovy.runtime.callsite.CallSiteClassLoader");
classesToSkip.add("sun.reflect.DelegatingClassLoader");
classesToSkip.add("jdk.internal.reflect.DelegatingClassLoader");
classesToSkip.add("clojure.lang.DynamicClassLoader");
classesToSkip.add("org.apache.cxf.common.util.ASMHelper$TypeHelperClassLoader");
classesToSkip.add(DatadogClassLoader.class.getName());
CLASSLOADER_CLASSES_TO_SKIP = Collections.unmodifiableSet(classesToSkip);
}
private SkipClassLoaderMatcher() {} private SkipClassLoaderMatcher() {}
@Override @Override
public boolean matches(final ClassLoader target) { public boolean matches(final ClassLoader cl) {
if (target == BOOTSTRAP_CLASSLOADER) { if (cl == BOOTSTRAP_CLASSLOADER) {
// Don't skip bootstrap loader // Don't skip bootstrap loader
return false; return false;
} }
return shouldSkipClass(target) || shouldSkipInstance(target); Boolean v = skipCache.getIfPresent(cl);
} if (v != null) {
return v;
private boolean shouldSkipClass(final ClassLoader loader) {
return CLASSLOADER_CLASSES_TO_SKIP.contains(loader.getClass().getName());
}
private boolean shouldSkipInstance(final ClassLoader loader) {
return SKIP_CACHE.computeIfAbsent(loader, this);
}
@Override
public Boolean get(final ClassLoader loader) {
final boolean skip = !delegatesToBootstrap(loader);
if (skip) {
log.debug(
"skipping classloader instance {} of type {}", loader, loader.getClass().getName());
} }
v = shouldSkipClass(cl) || !delegatesToBootstrap(cl);
skipCache.put(cl, v);
return v;
}
return skip; private static boolean shouldSkipClass(final ClassLoader loader) {
switch (loader.getClass().getName()) {
case "org.codehaus.groovy.runtime.callsite.CallSiteClassLoader":
case "sun.reflect.DelegatingClassLoader":
case "jdk.internal.reflect.DelegatingClassLoader":
case "clojure.lang.DynamicClassLoader":
case "org.apache.cxf.common.util.ASMHelper$TypeHelperClassLoader":
case "sun.misc.Launcher$ExtClassLoader":
case DATADOG_CLASSLOADER_NAME:
return true;
}
return false;
} }
/** /**
@ -85,7 +73,7 @@ public class ClassLoaderMatcher {
* class loading is issued from this check and {@code false} for 'real' class loads. We should * class loading is issued from this check and {@code false} for 'real' class loads. We should
* come up with some sort of hack to avoid this problem. * come up with some sort of hack to avoid this problem.
*/ */
private boolean delegatesToBootstrap(final ClassLoader loader) { private static boolean delegatesToBootstrap(final ClassLoader loader) {
boolean delegates = true; boolean delegates = true;
if (!loadsExpectedClass(loader, GlobalTracer.class)) { if (!loadsExpectedClass(loader, GlobalTracer.class)) {
log.debug("loader {} failed to delegate bootstrap opentracing class", loader); log.debug("loader {} failed to delegate bootstrap opentracing class", loader);
@ -98,7 +86,8 @@ public class ClassLoaderMatcher {
return delegates; return delegates;
} }
private boolean loadsExpectedClass(final ClassLoader loader, final Class<?> expectedClass) { private static boolean loadsExpectedClass(
final ClassLoader loader, final Class<?> expectedClass) {
try { try {
return loader.loadClass(expectedClass.getName()) == expectedClass; return loader.loadClass(expectedClass.getName()) == expectedClass;
} catch (final ClassNotFoundException e) { } catch (final ClassNotFoundException e) {
@ -107,36 +96,38 @@ public class ClassLoaderMatcher {
} }
} }
public static class ClassLoaderHasClassMatcher private static class ClassLoaderHasNoResourceMatcher
extends ElementMatcher.Junction.AbstractBase<ClassLoader> extends ElementMatcher.Junction.AbstractBase<ClassLoader> {
implements WeakMap.ValueSupplier<ClassLoader, Boolean> { private final Cache<ClassLoader, Boolean> cache =
CacheBuilder.newBuilder().weakKeys().concurrencyLevel(CACHE_CONCURRENCY).build();
private final WeakMap<ClassLoader, Boolean> cache = newWeakMap(); private final String[] resources;
private final String[] names; private ClassLoaderHasNoResourceMatcher(final String... resources) {
this.resources = resources;
private ClassLoaderHasClassMatcher(final String... names) {
this.names = names;
} }
@Override private boolean hasNoResources(ClassLoader cl) {
public boolean matches(final ClassLoader target) { for (final String resource : resources) {
if (target != null) { if (cl.getResource(resource) == null) {
return cache.computeIfAbsent(target, this); return true;
}
} }
return false; return false;
} }
@Override @Override
public Boolean get(final ClassLoader target) { public boolean matches(final ClassLoader cl) {
for (final String name : names) { if (cl == null) {
if (target.getResource(Utils.getResourceName(name)) == null) { return false;
return false;
}
} }
Boolean v = cache.getIfPresent(cl);
return true; if (v != null) {
return v;
}
v = hasNoResources(cl);
cache.put(cl, v);
return v;
} }
} }
} }

View File

@ -1,10 +1,16 @@
package datadog.trace.agent.tooling; package datadog.trace.agent.tooling;
import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.failSafe; import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.failSafe;
import static net.bytebuddy.matcher.ElementMatchers.any; import static net.bytebuddy.matcher.ElementMatchers.any;
import static net.bytebuddy.matcher.ElementMatchers.isAnnotatedWith;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.not;
import datadog.trace.agent.tooling.bytebuddy.DDTransformers;
import datadog.trace.agent.tooling.bytebuddy.ExceptionHandlers;
import datadog.trace.agent.tooling.context.FieldBackedProvider; import datadog.trace.agent.tooling.context.FieldBackedProvider;
import datadog.trace.agent.tooling.context.InstrumentationContextProvider; import datadog.trace.agent.tooling.context.InstrumentationContextProvider;
import datadog.trace.agent.tooling.context.NoopContextProvider;
import datadog.trace.agent.tooling.muzzle.Reference; import datadog.trace.agent.tooling.muzzle.Reference;
import datadog.trace.agent.tooling.muzzle.ReferenceMatcher; import datadog.trace.agent.tooling.muzzle.ReferenceMatcher;
import datadog.trace.api.Config; import datadog.trace.api.Config;
@ -17,9 +23,11 @@ import java.util.SortedSet;
import java.util.TreeSet; import java.util.TreeSet;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import net.bytebuddy.agent.builder.AgentBuilder; import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.description.annotation.AnnotationSource;
import net.bytebuddy.description.method.MethodDescription; import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher; import net.bytebuddy.matcher.ElementMatcher;
import net.bytebuddy.matcher.ElementMatcher.Junction;
import net.bytebuddy.utility.JavaModule; import net.bytebuddy.utility.JavaModule;
/** /**
@ -39,6 +47,12 @@ public interface Instrumenter {
@Slf4j @Slf4j
abstract class Default implements Instrumenter { abstract class Default implements Instrumenter {
// Added here instead of AgentInstaller's ignores because it's relatively
// expensive. https://github.com/DataDog/dd-trace-java/pull/1045
public static final Junction<AnnotationSource> NOT_DECORATOR_MATCHER =
not(isAnnotatedWith(named("javax.decorator.Decorator")));
private final SortedSet<String> instrumentationNames; private final SortedSet<String> instrumentationNames;
private final String instrumentationPrimaryName; private final String instrumentationPrimaryName;
private final InstrumentationContextProvider contextProvider; private final InstrumentationContextProvider contextProvider;
@ -53,7 +67,11 @@ public interface Instrumenter {
instrumentationPrimaryName = instrumentationName; instrumentationPrimaryName = instrumentationName;
enabled = Config.get().isIntegrationEnabled(instrumentationNames, defaultEnabled()); enabled = Config.get().isIntegrationEnabled(instrumentationNames, defaultEnabled());
contextProvider = new FieldBackedProvider(this); if (contextStore().size() > 0) {
contextProvider = new FieldBackedProvider(this);
} else {
contextProvider = NoopContextProvider.INSTANCE;
}
} }
@Override @Override
@ -73,6 +91,7 @@ public interface Instrumenter {
classLoaderMatcher(), classLoaderMatcher(),
"Instrumentation class loader matcher unexpected exception: " "Instrumentation class loader matcher unexpected exception: "
+ getClass().getName())) + getClass().getName()))
.and(NOT_DECORATOR_MATCHER)
.and(new MuzzleMatcher()) .and(new MuzzleMatcher())
.and(new PostMatchHook()) .and(new PostMatchHook())
.transform(DDTransformers.defaultTransformers()); .transform(DDTransformers.defaultTransformers());
@ -120,10 +139,11 @@ public interface Instrumenter {
*/ */
final ReferenceMatcher muzzle = getInstrumentationMuzzle(); final ReferenceMatcher muzzle = getInstrumentationMuzzle();
if (null != muzzle) { if (null != muzzle) {
final List<Reference.Mismatch> mismatches = final boolean isMatch = muzzle.matches(classLoader);
muzzle.getMismatchedReferenceSources(classLoader); if (!isMatch) {
if (mismatches.size() > 0) {
if (log.isDebugEnabled()) { if (log.isDebugEnabled()) {
final List<Reference.Mismatch> mismatches =
muzzle.getMismatchedReferenceSources(classLoader);
log.debug( log.debug(
"Instrumentation muzzled: {} -- {} on {}", "Instrumentation muzzled: {} -- {} on {}",
instrumentationNames, instrumentationNames,
@ -140,7 +160,7 @@ public interface Instrumenter {
Instrumenter.Default.this.getClass().getName(), Instrumenter.Default.this.getClass().getName(),
classLoader); classLoader);
} }
return mismatches.size() == 0; return isMatch;
} }
return true; return true;
} }
@ -204,11 +224,13 @@ public interface Instrumenter {
public abstract Map<? extends ElementMatcher<? super MethodDescription>, String> transformers(); public abstract Map<? extends ElementMatcher<? super MethodDescription>, String> transformers();
/** /**
* A map of {class-name -> context-class-name}. Keys (and their subclasses) will be associated * Context stores to define for this instrumentation.
* with a context of the value. *
* <p>A map of {class-name -> context-class-name}. Keys (and their subclasses) will be
* associated with a context of the value.
*/ */
public Map<String, String> contextStore() { public Map<String, String> contextStore() {
return Collections.EMPTY_MAP; return Collections.emptyMap();
} }
protected boolean defaultEnabled() { protected boolean defaultEnabled() {

View File

@ -39,7 +39,7 @@ class WeakMapSuppliers {
@Override @Override
public <K, V> WeakMap<K, V> get() { public <K, V> WeakMap<K, V> get() {
final WeakConcurrentMap<K, V> map = new WeakConcurrentMap<>(false); final WeakConcurrentMap<K, V> map = new WeakConcurrentMap<>(false, true);
cleaner.scheduleCleaning(map, MapCleaner.CLEANER, CLEAN_FREQUENCY_SECONDS, TimeUnit.SECONDS); cleaner.scheduleCleaning(map, MapCleaner.CLEANER, CLEAN_FREQUENCY_SECONDS, TimeUnit.SECONDS);
return new Adapter<>(map); return new Adapter<>(map);
} }

View File

@ -1,4 +1,4 @@
package datadog.trace.agent.tooling; package datadog.trace.agent.tooling.bytebuddy;
import static net.bytebuddy.agent.builder.AgentBuilder.PoolStrategy; import static net.bytebuddy.agent.builder.AgentBuilder.PoolStrategy;
@ -6,7 +6,11 @@ import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheBuilder;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import net.bytebuddy.description.annotation.AnnotationList;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.method.MethodList;
import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.description.type.TypeList;
import net.bytebuddy.dynamic.ClassFileLocator; import net.bytebuddy.dynamic.ClassFileLocator;
import net.bytebuddy.pool.TypePool; import net.bytebuddy.pool.TypePool;
@ -42,7 +46,7 @@ public class DDCachingPoolStrategy implements PoolStrategy {
static final int LOADER_CAPACITY = 64; static final int LOADER_CAPACITY = 64;
static final int TYPE_CAPACITY = 64; static final int TYPE_CAPACITY = 64;
static final int BOOTSTRAP_HASH = 0; static final int BOOTSTRAP_HASH = 7236344; // Just a random number
/** /**
* Cache of recent ClassLoader WeakReferences; used to... * Cache of recent ClassLoader WeakReferences; used to...
@ -89,16 +93,16 @@ public class DDCachingPoolStrategy implements PoolStrategy {
loaderRefCache.put(classLoader, loaderRef); loaderRefCache.put(classLoader, loaderRef);
} }
int loaderHash = classLoader.hashCode(); final int loaderHash = classLoader.hashCode();
return createCachingTypePool(loaderHash, loaderRef, classFileLocator); return createCachingTypePool(loaderHash, loaderRef, classFileLocator);
} }
private final TypePool.CacheProvider createCacheProvider( private TypePool.CacheProvider createCacheProvider(
final int loaderHash, final WeakReference<ClassLoader> loaderRef) { final int loaderHash, final WeakReference<ClassLoader> loaderRef) {
return new SharedResolutionCacheAdapter(loaderHash, loaderRef, sharedResolutionCache); return new SharedResolutionCacheAdapter(loaderHash, loaderRef, sharedResolutionCache);
} }
private final TypePool createCachingTypePool( private TypePool createCachingTypePool(
final int loaderHash, final int loaderHash,
final WeakReference<ClassLoader> loaderRef, final WeakReference<ClassLoader> loaderRef,
final ClassFileLocator classFileLocator) { final ClassFileLocator classFileLocator) {
@ -108,7 +112,7 @@ public class DDCachingPoolStrategy implements PoolStrategy {
TypePool.Default.ReaderMode.FAST); TypePool.Default.ReaderMode.FAST);
} }
private final TypePool createCachingTypePool( private TypePool createCachingTypePool(
final TypePool.CacheProvider cacheProvider, final ClassFileLocator classFileLocator) { final TypePool.CacheProvider cacheProvider, final ClassFileLocator classFileLocator) {
return new TypePool.Default.WithLazyResolution( return new TypePool.Default.WithLazyResolution(
cacheProvider, classFileLocator, TypePool.Default.ReaderMode.FAST); cacheProvider, classFileLocator, TypePool.Default.ReaderMode.FAST);
@ -140,7 +144,7 @@ public class DDCachingPoolStrategy implements PoolStrategy {
this.loaderRef = loaderRef; this.loaderRef = loaderRef;
this.className = className; this.className = className;
hashCode = (int) (31 * this.loaderHash) ^ className.hashCode(); hashCode = 31 * this.loaderHash + className.hashCode();
} }
@Override @Override
@ -150,18 +154,23 @@ public class DDCachingPoolStrategy implements PoolStrategy {
@Override @Override
public boolean equals(final Object obj) { public boolean equals(final Object obj) {
if (!(obj instanceof TypeCacheKey)) return false; if (!(obj instanceof TypeCacheKey)) {
return false;
}
TypeCacheKey that = (TypeCacheKey) obj; final TypeCacheKey that = (TypeCacheKey) obj;
if (loaderHash != that.loaderHash) return false; if (loaderHash != that.loaderHash) {
return false;
}
if (className.equals(that.className)) {
// Fastpath loaderRef equivalence -- works because of WeakReference cache used
// Also covers the bootstrap null loaderRef case
if (loaderRef == that.loaderRef) {
return true;
}
// Fastpath loaderRef equivalence -- works because of WeakReference cache used
// Also covers the bootstrap null loaderRef case
if (loaderRef == that.loaderRef) {
// still need to check name
return className.equals(that.className);
} else if (className.equals(that.className)) {
// need to perform a deeper loader check -- requires calling Reference.get // need to perform a deeper loader check -- requires calling Reference.get
// which can strengthen the Reference, so deliberately done last // which can strengthen the Reference, so deliberately done last
@ -172,11 +181,15 @@ public class DDCachingPoolStrategy implements PoolStrategy {
// In this case, it is fine because that means the ClassLoader is no // In this case, it is fine because that means the ClassLoader is no
// longer live, so the entries will never match anyway and will fall // longer live, so the entries will never match anyway and will fall
// out of the cache. // out of the cache.
ClassLoader thisLoader = loaderRef.get(); final ClassLoader thisLoader = loaderRef.get();
if (thisLoader == null) return false; if (thisLoader == null) {
return false;
}
ClassLoader thatLoader = that.loaderRef.get(); final ClassLoader thatLoader = that.loaderRef.get();
if (thatLoader == null) return false; if (thatLoader == null) {
return false;
}
return (thisLoader == thatLoader); return (thisLoader == thatLoader);
} else { } else {
@ -188,7 +201,7 @@ public class DDCachingPoolStrategy implements PoolStrategy {
static final class SharedResolutionCacheAdapter implements TypePool.CacheProvider { static final class SharedResolutionCacheAdapter implements TypePool.CacheProvider {
private static final String OBJECT_NAME = "java.lang.Object"; private static final String OBJECT_NAME = "java.lang.Object";
private static final TypePool.Resolution OBJECT_RESOLUTION = private static final TypePool.Resolution OBJECT_RESOLUTION =
new TypePool.Resolution.Simple(TypeDescription.OBJECT); new TypePool.Resolution.Simple(new CachingTypeDescription(TypeDescription.OBJECT));
private final int loaderHash; private final int loaderHash;
private final WeakReference<ClassLoader> loaderRef; private final WeakReference<ClassLoader> loaderRef;
@ -205,9 +218,11 @@ public class DDCachingPoolStrategy implements PoolStrategy {
@Override @Override
public TypePool.Resolution find(final String className) { public TypePool.Resolution find(final String className) {
TypePool.Resolution existingResolution = final TypePool.Resolution existingResolution =
sharedResolutionCache.getIfPresent(new TypeCacheKey(loaderHash, loaderRef, className)); sharedResolutionCache.getIfPresent(new TypeCacheKey(loaderHash, loaderRef, className));
if (existingResolution != null) return existingResolution; if (existingResolution != null) {
return existingResolution;
}
if (OBJECT_NAME.equals(className)) { if (OBJECT_NAME.equals(className)) {
return OBJECT_RESOLUTION; return OBJECT_RESOLUTION;
@ -217,12 +232,13 @@ public class DDCachingPoolStrategy implements PoolStrategy {
} }
@Override @Override
public TypePool.Resolution register( public TypePool.Resolution register(final String className, TypePool.Resolution resolution) {
final String className, final TypePool.Resolution resolution) {
if (OBJECT_NAME.equals(className)) { if (OBJECT_NAME.equals(className)) {
return resolution; return resolution;
} }
resolution = new CachingResolution(resolution);
sharedResolutionCache.put(new TypeCacheKey(loaderHash, loaderRef, className), resolution); sharedResolutionCache.put(new TypeCacheKey(loaderHash, loaderRef, className), resolution);
return resolution; return resolution;
} }
@ -232,4 +248,90 @@ public class DDCachingPoolStrategy implements PoolStrategy {
// Allowing the high-level eviction policy make the clearing decisions // Allowing the high-level eviction policy make the clearing decisions
} }
} }
private static class CachingResolution implements TypePool.Resolution {
private final TypePool.Resolution delegate;
private TypeDescription cachedResolution;
public CachingResolution(final TypePool.Resolution delegate) {
this.delegate = delegate;
}
@Override
public boolean isResolved() {
return delegate.isResolved();
}
@Override
public TypeDescription resolve() {
// Intentionally not "thread safe". Duplicate work deemed an acceptable trade-off.
if (cachedResolution == null) {
cachedResolution = new CachingTypeDescription(delegate.resolve());
}
return cachedResolution;
}
}
/**
* TypeDescription implementation that delegates and caches the results for the expensive calls
* commonly used by our instrumentation.
*/
private static class CachingTypeDescription
extends TypeDescription.AbstractBase.OfSimpleType.WithDelegation {
private final TypeDescription delegate;
// These fields are intentionally not "thread safe".
// Duplicate work deemed an acceptable trade-off.
private Generic superClass;
private TypeList.Generic interfaces;
private AnnotationList annotations;
private MethodList<MethodDescription.InDefinedShape> methods;
public CachingTypeDescription(final TypeDescription delegate) {
this.delegate = delegate;
}
@Override
protected TypeDescription delegate() {
return delegate;
}
@Override
public Generic getSuperClass() {
if (superClass == null) {
superClass = delegate.getSuperClass();
}
return superClass;
}
@Override
public TypeList.Generic getInterfaces() {
if (interfaces == null) {
interfaces = delegate.getInterfaces();
}
return interfaces;
}
@Override
public AnnotationList getDeclaredAnnotations() {
if (annotations == null) {
annotations = delegate.getDeclaredAnnotations();
}
return annotations;
}
@Override
public MethodList<MethodDescription.InDefinedShape> getDeclaredMethods() {
if (methods == null) {
methods = delegate.getDeclaredMethods();
}
return methods;
}
@Override
public String getName() {
return delegate.getName();
}
}
} }

View File

@ -1,5 +1,6 @@
package datadog.trace.agent.tooling; package datadog.trace.agent.tooling.bytebuddy;
import datadog.trace.agent.tooling.Utils;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import net.bytebuddy.agent.builder.AgentBuilder; import net.bytebuddy.agent.builder.AgentBuilder;

View File

@ -1,4 +1,4 @@
package datadog.trace.agent.tooling; package datadog.trace.agent.tooling.bytebuddy;
import net.bytebuddy.agent.builder.AgentBuilder; import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.asm.TypeConstantAdjustment; import net.bytebuddy.asm.TypeConstantAdjustment;
@ -12,10 +12,10 @@ public class DDTransformers {
new AgentBuilder.Transformer() { new AgentBuilder.Transformer() {
@Override @Override
public DynamicType.Builder<?> transform( public DynamicType.Builder<?> transform(
DynamicType.Builder<?> builder, final DynamicType.Builder<?> builder,
TypeDescription typeDescription, final TypeDescription typeDescription,
ClassLoader classLoader, final ClassLoader classLoader,
JavaModule javaModule) { final JavaModule javaModule) {
return builder.visit(TypeConstantAdjustment.INSTANCE); return builder.visit(TypeConstantAdjustment.INSTANCE);
} }
}; };

View File

@ -1,4 +1,4 @@
package datadog.trace.agent.tooling; package datadog.trace.agent.tooling.bytebuddy;
import datadog.trace.bootstrap.ExceptionLogger; import datadog.trace.bootstrap.ExceptionLogger;
import net.bytebuddy.ClassFileVersion; import net.bytebuddy.ClassFileVersion;

View File

@ -0,0 +1,162 @@
package datadog.trace.agent.tooling.bytebuddy;
import java.util.regex.Pattern;
import net.bytebuddy.build.HashCodeAndEqualsPlugin;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
@HashCodeAndEqualsPlugin.Enhance
public class GlobalIgnoresMatcher<T extends TypeDescription>
extends ElementMatcher.Junction.AbstractBase<T> {
private static final Pattern COM_MCHANGE_PROXY =
Pattern.compile("com\\.mchange\\.v2\\.c3p0\\..*Proxy");
public static <T extends TypeDescription> ElementMatcher.Junction<T> globalIgnoresMatcher() {
return new GlobalIgnoresMatcher<>();
}
/**
* Be very careful about the types of matchers used in this section as they are called on every
* class load, so they must be fast. Generally speaking try to only use name matchers as they
* don't have to load additional info.
*/
@Override
public boolean matches(final T target) {
final String name = target.getActualName();
if (name.startsWith("datadog.opentracing.")
|| name.startsWith("datadog.slf4j.")
|| name.startsWith("net.bytebuddy.")
|| name.startsWith("jdk.")
|| name.startsWith("org.aspectj.")
|| name.startsWith("org.groovy.")
|| name.startsWith("org.codehaus.groovy.macro.")
|| name.startsWith("com.intellij.rt.debugger.")
|| name.startsWith("com.p6spy.")
|| name.startsWith("com.newrelic.")
|| name.startsWith("com.dynatrace.")
|| name.startsWith("com.jloadtrace.")
|| name.startsWith("com.appdynamics.")
|| name.startsWith("com.singularity.")
|| name.startsWith("com.jinspired.")
|| name.startsWith("org.jinspired.")
|| name.startsWith("org.springframework.cglib.")
|| name.startsWith("org.springframework.aop.")
|| name.startsWith("org.springframework.beans.factory.annotation.")
|| name.startsWith("org.springframework.beans.factory.config.")
|| name.startsWith("org.springframework.beans.factory.parsing.")
|| name.startsWith("org.springframework.beans.factory.xml.")
|| name.startsWith("org.springframework.beans.propertyeditors.")
|| name.startsWith("org.springframework.boot.autoconfigure.cache.")
|| name.startsWith("org.springframework.boot.autoconfigure.condition.")
|| name.startsWith("org.springframework.boot.autoconfigure.http.")
|| name.startsWith("org.springframework.boot.autoconfigure.jackson.")
|| name.startsWith("org.springframework.boot.autoconfigure.web.")
|| name.startsWith("org.springframework.boot.context.")
|| name.startsWith("org.springframework.boot.convert.")
|| name.startsWith("org.springframework.boot.diagnostics.")
|| name.startsWith("org.springframework.boot.web.server.")
|| name.startsWith("org.springframework.boot.web.servlet.")
|| name.startsWith("org.springframework.context.annotation.")
|| name.startsWith("org.springframework.context.event.")
|| name.startsWith("org.springframework.context.expression.")
|| name.startsWith("org.springframework.core.annotation.")
|| name.startsWith("org.springframework.core.convert.")
|| name.startsWith("org.springframework.core.env.")
|| name.startsWith("org.springframework.core.io.")
|| name.startsWith("org.springframework.core.type.")
|| name.startsWith("org.springframework.expression.")
|| name.startsWith("org.springframework.format.")
|| name.startsWith("org.springframework.http.")
|| name.startsWith("org.springframework.ui.")
|| name.startsWith("org.springframework.validation.")
|| name.startsWith("org.springframework.web.context.")
|| name.startsWith("org.springframework.web.filter.")
|| name.startsWith("org.springframework.web.method.")
|| name.startsWith("org.springframework.web.multipart.")
|| name.startsWith("org.springframework.web.util.")) {
return true;
}
if (name.startsWith("datadog.trace.")) {
// FIXME: We should remove this once
// https://github.com/raphw/byte-buddy/issues/558 is fixed
if (name.equals("datadog.trace.bootstrap.instrumentation.java.concurrent.RunnableWrapper")
|| name.equals(
"datadog.trace.bootstrap.instrumentation.java.concurrent.CallableWrapper")) {
return false;
}
return true;
}
if (name.startsWith("java.")) {
if (name.equals("java.net.URL") || name.equals("java.net.HttpURLConnection")) {
return false;
}
if (name.startsWith("java.rmi.") || name.startsWith("java.util.concurrent.")) {
return false;
}
// Concurrent instrumentation modifies the structure of
// Cleaner class incompatibly with java9+ modules.
// Working around until a long-term fix for modules can be
// put in place.
if (name.startsWith("java.util.logging.")
&& !name.equals("java.util.logging.LogManager$Cleaner")) {
return false;
}
return true;
}
if (name.startsWith("com.sun.")) {
if (name.startsWith("com.sun.messaging.") || name.startsWith("com.sun.jersey.api.client")) {
return false;
}
return true;
}
if (name.startsWith("sun.")) {
if (name.startsWith("sun.net.www.protocol.")
|| name.startsWith("sun.rmi.server")
|| name.startsWith("sun.rmi.transport")
|| name.equals("sun.net.www.http.HttpClient")) {
return false;
}
return true;
}
if (name.startsWith("org.apache.log4j.")) {
if (name.equals("org.apache.log4j.MDC")) {
return false;
}
return true;
}
if (name.startsWith("org.slf4j.")) {
if (name.equals("org.slf4j.MDC")) {
return false;
}
return true;
}
if (name.contains("$JaxbAccessor")
|| name.contains("CGLIB$$")
|| name.contains("javassist")
|| name.contains(".asm.")
|| name.contains("$__sisu")) {
return true;
}
if (COM_MCHANGE_PROXY.matcher(name).matches()) {
return true;
}
return false;
}
}

View File

@ -0,0 +1,72 @@
package datadog.trace.agent.tooling.bytebuddy.matcher;
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
import static net.bytebuddy.matcher.ElementMatchers.not;
import lombok.extern.slf4j.Slf4j;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.type.TypeDefinition;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
/**
* This class provides some custom ByteBuddy element matchers to use when applying instrumentation
*/
@Slf4j
public class DDElementMatchers {
public static <T extends TypeDescription> ElementMatcher.Junction<T> extendsClass(
final ElementMatcher<? super TypeDescription> matcher) {
return not(isInterface()).and(new SafeExtendsClassMatcher<>(new SafeErasureMatcher<>(matcher)));
}
public static <T extends TypeDescription> ElementMatcher.Junction<T> implementsInterface(
final ElementMatcher<? super TypeDescription> matcher) {
return not(isInterface())
.and(new SafeHasSuperTypeMatcher<>(new SafeErasureMatcher<>(matcher), true));
}
public static <T extends TypeDescription> ElementMatcher.Junction<T> hasInterface(
final ElementMatcher<? super TypeDescription> matcher) {
return new SafeHasSuperTypeMatcher<>(new SafeErasureMatcher<>(matcher), true);
}
public static <T extends TypeDescription> ElementMatcher.Junction<T> safeHasSuperType(
final ElementMatcher<? super TypeDescription> matcher) {
return not(isInterface())
.and(new SafeHasSuperTypeMatcher<>(new SafeErasureMatcher<>(matcher), false));
}
// TODO: add javadoc
public static <T extends MethodDescription> ElementMatcher.Junction<T> hasSuperMethod(
final ElementMatcher<? super MethodDescription> matcher) {
return new HasSuperMethodMatcher<>(matcher);
}
/**
* Wraps another matcher to assure that an element is not matched in case that the matching causes
* an {@link Exception}. Logs exception if it happens.
*
* @param matcher The element matcher that potentially throws an exception.
* @param <T> The type of the matched object.
* @return A matcher that returns {@code false} in case that the given matcher throws an
* exception.
*/
public static <T> ElementMatcher.Junction<T> failSafe(
final ElementMatcher<? super T> matcher, final String description) {
return new LoggingFailSafeMatcher<>(matcher, false, description);
}
static String safeTypeDefinitionName(final TypeDefinition td) {
try {
return td.getTypeName();
} catch (final IllegalStateException ex) {
final String message = ex.getMessage();
if (message.startsWith("Cannot resolve type description for ")) {
return message.replace("Cannot resolve type description for ", "");
} else {
return "?";
}
}
}
}

View File

@ -0,0 +1,72 @@
package datadog.trace.agent.tooling.bytebuddy.matcher;
import static datadog.trace.agent.tooling.bytebuddy.matcher.SafeHasSuperTypeMatcher.safeGetSuperClass;
import static net.bytebuddy.matcher.ElementMatchers.hasSignature;
import java.util.HashSet;
import java.util.Set;
import net.bytebuddy.build.HashCodeAndEqualsPlugin;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.type.TypeDefinition;
import net.bytebuddy.description.type.TypeList;
import net.bytebuddy.matcher.ElementMatcher;
// TODO: add javadoc
@HashCodeAndEqualsPlugin.Enhance
class HasSuperMethodMatcher<T extends MethodDescription>
extends ElementMatcher.Junction.AbstractBase<T> {
private final ElementMatcher<? super MethodDescription> matcher;
public HasSuperMethodMatcher(final ElementMatcher<? super MethodDescription> matcher) {
this.matcher = matcher;
}
@Override
public boolean matches(final MethodDescription target) {
if (target.isConstructor()) {
return false;
}
final Junction<MethodDescription> signatureMatcher = hasSignature(target.asSignatureToken());
TypeDefinition declaringType = target.getDeclaringType();
final Set<TypeDefinition> checkedInterfaces = new HashSet<>();
while (declaringType != null) {
for (final MethodDescription methodDescription : declaringType.getDeclaredMethods()) {
if (signatureMatcher.matches(methodDescription) && matcher.matches(methodDescription)) {
return true;
}
}
if (matchesInterface(declaringType.getInterfaces(), signatureMatcher, checkedInterfaces)) {
return true;
}
declaringType = safeGetSuperClass(declaringType);
}
return false;
}
private boolean matchesInterface(
final TypeList.Generic interfaces,
final Junction<MethodDescription> signatureMatcher,
final Set<TypeDefinition> checkedInterfaces) {
for (final TypeDefinition type : interfaces) {
if (!checkedInterfaces.contains(type)) {
checkedInterfaces.add(type);
for (final MethodDescription methodDescription : type.getDeclaredMethods()) {
if (signatureMatcher.matches(methodDescription) && matcher.matches(methodDescription)) {
return true;
}
}
if (matchesInterface(type.getInterfaces(), signatureMatcher, checkedInterfaces)) {
return true;
}
}
}
return false;
}
@Override
public String toString() {
return "hasSuperMethodMatcher(" + matcher + ")";
}
}

View File

@ -0,0 +1,57 @@
package datadog.trace.agent.tooling.bytebuddy.matcher;
import lombok.extern.slf4j.Slf4j;
import net.bytebuddy.build.HashCodeAndEqualsPlugin;
import net.bytebuddy.matcher.ElementMatcher;
/**
* A fail-safe matcher catches exceptions that are thrown by a delegate matcher and returns an
* alternative value.
*
* <p>Logs exception if it was thrown.
*
* @param <T> The type of the matched entity.
* @see net.bytebuddy.matcher.FailSafeMatcher
*/
@Slf4j
@HashCodeAndEqualsPlugin.Enhance
class LoggingFailSafeMatcher<T> extends ElementMatcher.Junction.AbstractBase<T> {
/** The delegate matcher that might throw an exception. */
private final ElementMatcher<? super T> matcher;
/** The fallback value in case of an exception. */
private final boolean fallback;
/** The text description to log if exception happens. */
private final String description;
/**
* Creates a new fail-safe element matcher.
*
* @param matcher The delegate matcher that might throw an exception.
* @param fallback The fallback value in case of an exception.
* @param description Descriptive string to log along with exception.
*/
public LoggingFailSafeMatcher(
final ElementMatcher<? super T> matcher, final boolean fallback, final String description) {
this.matcher = matcher;
this.fallback = fallback;
this.description = description;
}
@Override
public boolean matches(final T target) {
try {
return matcher.matches(target);
} catch (final Exception e) {
log.debug(description, e);
return fallback;
}
}
@Override
public String toString() {
return "safeMatcher(try(" + matcher + ") or " + fallback + ")";
}
}

View File

@ -0,0 +1,65 @@
package datadog.trace.agent.tooling.bytebuddy.matcher;
import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.safeTypeDefinitionName;
import lombok.extern.slf4j.Slf4j;
import net.bytebuddy.build.HashCodeAndEqualsPlugin;
import net.bytebuddy.description.type.TypeDefinition;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
/**
* An element matcher that matches its argument's {@link TypeDescription.Generic} raw type against
* the given matcher for a {@link TypeDescription}. As a wildcard does not define an erasure, a
* runtime exception is thrown when this matcher is applied to a wildcard.
*
* <p>Catches and logs exception if it was thrown when getting erasure, returning false.
*
* @param <T> The type of the matched entity.
* @see net.bytebuddy.matcher.ErasureMatcher
*/
@Slf4j
@HashCodeAndEqualsPlugin.Enhance
class SafeErasureMatcher<T extends TypeDefinition> extends ElementMatcher.Junction.AbstractBase<T> {
/** The matcher to apply to the raw type of the matched element. */
private final ElementMatcher<? super TypeDescription> matcher;
/**
* Creates a new erasure matcher.
*
* @param matcher The matcher to apply to the raw type.
*/
public SafeErasureMatcher(final ElementMatcher<? super TypeDescription> matcher) {
this.matcher = matcher;
}
@Override
public boolean matches(final T target) {
final TypeDescription erasure = safeAsErasure(target);
if (erasure == null) {
return false;
} else {
// We would like matcher exceptions to propagate
return matcher.matches(erasure);
}
}
@Override
public String toString() {
return "safeErasure(" + matcher + ")";
}
static TypeDescription safeAsErasure(final TypeDefinition typeDefinition) {
try {
return typeDefinition.asErasure();
} catch (final Exception e) {
log.debug(
"{} trying to get erasure for target {}: {}",
e.getClass().getSimpleName(),
safeTypeDefinitionName(typeDefinition),
e.getMessage());
return null;
}
}
}

View File

@ -0,0 +1,36 @@
package datadog.trace.agent.tooling.bytebuddy.matcher;
import static datadog.trace.agent.tooling.bytebuddy.matcher.SafeHasSuperTypeMatcher.safeGetSuperClass;
import net.bytebuddy.description.type.TypeDefinition;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
class SafeExtendsClassMatcher<T extends TypeDescription>
extends ElementMatcher.Junction.AbstractBase<T> {
private final ElementMatcher<? super TypeDescription.Generic> matcher;
public SafeExtendsClassMatcher(final ElementMatcher<? super TypeDescription.Generic> matcher) {
this.matcher = matcher;
}
@Override
public boolean matches(final T target) {
// We do not use foreach loop and iterator interface here because we need to catch exceptions
// in {@code getSuperClass} calls
TypeDefinition typeDefinition = target;
while (typeDefinition != null) {
if (matcher.matches(typeDefinition.asGenericType())) {
return true;
}
typeDefinition = safeGetSuperClass(typeDefinition);
}
return false;
}
@Override
public String toString() {
return "safeExtendsClass(" + matcher + ")";
}
}

View File

@ -0,0 +1,135 @@
package datadog.trace.agent.tooling.bytebuddy.matcher;
import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.safeTypeDefinitionName;
import static datadog.trace.agent.tooling.bytebuddy.matcher.SafeErasureMatcher.safeAsErasure;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import lombok.extern.slf4j.Slf4j;
import net.bytebuddy.build.HashCodeAndEqualsPlugin;
import net.bytebuddy.description.type.TypeDefinition;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
/**
* An element matcher that matches a super type. This is different from {@link
* net.bytebuddy.matcher.HasSuperTypeMatcher} in the following way:
*
* <ul>
* <li>Exceptions are logged
* <li>When exception happens the rest of the inheritance subtree is discarded (since ByteBuddy
* cannot load/parse type information for it) but search in other subtrees continues
* </ul>
*
* <p>This is useful because this allows us to see when matcher's check is not complete (i.e. part
* of it fails), at the same time it makes best effort instead of failing quickly (like {@code
* failSafe(hasSuperType(...))} does) which means the code is more resilient to classpath
* inconsistencies
*
* @param <T> The type of the matched entity.
* @see net.bytebuddy.matcher.HasSuperTypeMatcher
*/
@Slf4j
@HashCodeAndEqualsPlugin.Enhance
class SafeHasSuperTypeMatcher<T extends TypeDescription>
extends ElementMatcher.Junction.AbstractBase<T> {
/** The matcher to apply to any super type of the matched type. */
private final ElementMatcher<? super TypeDescription.Generic> matcher;
private final boolean interfacesOnly;
/**
* Creates a new matcher for a super type.
*
* @param matcher The matcher to apply to any super type of the matched type.
*/
public SafeHasSuperTypeMatcher(
final ElementMatcher<? super TypeDescription.Generic> matcher, final boolean interfacesOnly) {
this.matcher = matcher;
this.interfacesOnly = interfacesOnly;
}
@Override
public boolean matches(final T target) {
final Set<TypeDescription> checkedInterfaces = new HashSet<>();
// We do not use foreach loop and iterator interface here because we need to catch exceptions
// in {@code getSuperClass} calls
TypeDefinition typeDefinition = target;
while (typeDefinition != null) {
if (((!interfacesOnly || typeDefinition.isInterface())
&& matcher.matches(typeDefinition.asGenericType()))
|| hasInterface(typeDefinition, checkedInterfaces)) {
return true;
}
typeDefinition = safeGetSuperClass(typeDefinition);
}
return false;
}
/**
* Matches a type's interfaces against the provided matcher.
*
* @param typeDefinition The type for which to check all implemented interfaces.
* @param checkedInterfaces The interfaces that have already been checked.
* @return {@code true} if any interface matches the supplied matcher.
*/
private boolean hasInterface(
final TypeDefinition typeDefinition, final Set<TypeDescription> checkedInterfaces) {
for (final TypeDefinition interfaceType : safeGetInterfaces(typeDefinition)) {
final TypeDescription erasure = safeAsErasure(interfaceType);
if (erasure != null) {
if (checkedInterfaces.add(interfaceType.asErasure())
&& (matcher.matches(interfaceType.asGenericType())
|| hasInterface(interfaceType, checkedInterfaces))) {
return true;
}
}
}
return false;
}
/**
* TypeDefinition#getInterfaces() produces an interator which may throw an exception during
* iteration if an interface is absent from the classpath.
*
* <p>This method exists to allow getting interfaces even if the lookup on one fails.
*/
private List<TypeDefinition> safeGetInterfaces(final TypeDefinition typeDefinition) {
final List<TypeDefinition> interfaceTypes = new ArrayList<>();
try {
final Iterator<TypeDescription.Generic> interfaceIter =
typeDefinition.getInterfaces().iterator();
while (interfaceIter.hasNext()) {
interfaceTypes.add(interfaceIter.next());
}
} catch (final Exception e) {
log.debug(
"{} trying to get interfaces for target {}: {}",
e.getClass().getSimpleName(),
safeTypeDefinitionName(typeDefinition),
e.getMessage());
}
return interfaceTypes;
}
static TypeDefinition safeGetSuperClass(final TypeDefinition typeDefinition) {
try {
return typeDefinition.getSuperClass();
} catch (final Exception e) {
log.debug(
"{} trying to get super class for target {}: {}",
e.getClass().getSimpleName(),
safeTypeDefinitionName(typeDefinition),
e.getMessage());
return null;
}
}
@Override
public String toString() {
return "safeHasSuperType(" + matcher + ")";
}
}

View File

@ -1,13 +1,12 @@
package datadog.trace.agent.tooling.context; package datadog.trace.agent.tooling.context;
import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.safeHasSuperType;
import static datadog.trace.agent.tooling.ClassLoaderMatcher.BOOTSTRAP_CLASSLOADER; import static datadog.trace.agent.tooling.ClassLoaderMatcher.BOOTSTRAP_CLASSLOADER;
import static net.bytebuddy.matcher.ElementMatchers.isInterface; import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.safeHasSuperType;
import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.not;
import datadog.trace.agent.tooling.HelperInjector; import datadog.trace.agent.tooling.HelperInjector;
import datadog.trace.agent.tooling.Instrumenter; import datadog.trace.agent.tooling.Instrumenter;
import datadog.trace.agent.tooling.Instrumenter.Default;
import datadog.trace.agent.tooling.Utils; import datadog.trace.agent.tooling.Utils;
import datadog.trace.api.Config; import datadog.trace.api.Config;
import datadog.trace.bootstrap.ContextStore; import datadog.trace.bootstrap.ContextStore;
@ -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;
@ -126,8 +126,8 @@ public class FieldBackedProvider implements InstrumentationContextProvider {
@Override @Override
public AgentBuilder.Identified.Extendable instrumentationTransformer( public AgentBuilder.Identified.Extendable instrumentationTransformer(
AgentBuilder.Identified.Extendable builder) { AgentBuilder.Identified.Extendable builder) {
if (instrumenter.contextStore().size() > 0) { if (!instrumenter.contextStore().isEmpty()) {
/** /*
* Install transformer that rewrites accesses to context store with specialized bytecode that * Install transformer that rewrites accesses to context store with specialized bytecode that
* invokes appropriate storage implementation. * invokes appropriate storage implementation.
*/ */
@ -303,14 +303,14 @@ public class FieldBackedProvider implements InstrumentationContextProvider {
private AgentBuilder.Identified.Extendable injectHelpersIntoBootstrapClassloader( private AgentBuilder.Identified.Extendable injectHelpersIntoBootstrapClassloader(
AgentBuilder.Identified.Extendable builder) { AgentBuilder.Identified.Extendable builder) {
/** /*
* We inject into bootstrap classloader because field accessor interfaces are needed by context * We inject into bootstrap classloader because field accessor interfaces are needed by context
* store implementations. Unfortunately this forces us to remove stored type checking because * store implementations. Unfortunately this forces us to remove stored type checking because
* actual classes may not be available at this point. * actual classes may not be available at this point.
*/ */
builder = builder.transform(fieldAccessorInterfacesInjector); builder = builder.transform(fieldAccessorInterfacesInjector);
/** /*
* We inject context store implementation into bootstrap classloader because same implementation * We inject context store implementation into bootstrap classloader because same implementation
* may be used by different instrumentations and it has to use same static map in case of * may be used by different instrumentations and it has to use same static map in case of
* fallback to map-backed storage. * fallback to map-backed storage.
@ -341,35 +341,69 @@ 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) {
if (fieldInjectionEnabled) { if (fieldInjectionEnabled) {
for (final Map.Entry<String, String> entry : instrumenter.contextStore().entrySet()) { for (final Map.Entry<String, String> entry : instrumenter.contextStore().entrySet()) {
/** /*
* 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( // same context classes should be the same - which seems reasonable, but is not checked.
safeHasSuperType(named(entry.getKey())).and(not(isInterface())), // Addressing this properly requires some notion of 'compound intrumenters' which we
instrumenter.classLoaderMatcher()) // currently do not have.
.and(safeToInjectFieldsMatcher()) if (INSTALLED_CONTEXT_MATCHERS.contains(entry)) {
.transform(AgentBuilder.Transformer.NoOp.INSTANCE); 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;
@ -384,7 +418,7 @@ public class FieldBackedProvider implements InstrumentationContextProvider {
final JavaModule module, final JavaModule module,
final Class<?> classBeingRedefined, final Class<?> classBeingRedefined,
final ProtectionDomain protectionDomain) { final ProtectionDomain protectionDomain) {
/** /*
* The idea here is that we can add fields if class is just being loaded * The idea here is that we can add fields if class is just being loaded
* (classBeingRedefined == null) and we have to add same fields again if class we added * (classBeingRedefined == null) and we have to add same fields again if class we added
* fields before is being transformed again. Note: here we assume that Class#getInterfaces() * fields before is being transformed again. Note: here we assume that Class#getInterfaces()

View File

@ -0,0 +1,20 @@
package datadog.trace.agent.tooling.context;
import net.bytebuddy.agent.builder.AgentBuilder.Identified.Extendable;
public class NoopContextProvider implements InstrumentationContextProvider {
public static NoopContextProvider INSTANCE = new NoopContextProvider();
private NoopContextProvider() {}
@Override
public Extendable instrumentationTransformer(final Extendable builder) {
return builder;
}
@Override
public Extendable additionalInstrumentation(final Extendable builder) {
return builder;
}
}

View File

@ -22,9 +22,8 @@ import net.bytebuddy.pool.TypePool;
/** Matches a set of references against a classloader. */ /** Matches a set of references against a classloader. */
@Slf4j @Slf4j
public class ReferenceMatcher public final class ReferenceMatcher implements WeakMap.ValueSupplier<ClassLoader, Boolean> {
implements WeakMap.ValueSupplier<ClassLoader, List<Reference.Mismatch>> { private final WeakMap<ClassLoader, Boolean> mismatchCache = newWeakMap();
private final WeakMap<ClassLoader, List<Reference.Mismatch>> mismatchCache = newWeakMap();
private final Reference[] references; private final Reference[] references;
private final Set<String> helperClassNames; private final Set<String> helperClassNames;
@ -42,18 +41,12 @@ public class ReferenceMatcher
} }
/** /**
* Matcher used by ByteBuddy. Fails fast and only caches empty results, or complete results
*
* @param loader Classloader to validate against (or null for bootstrap) * @param loader Classloader to validate against (or null for bootstrap)
* @return true if all references match the classpath of loader * @return true if all references match the classpath of loader
*/ */
public boolean matches(final ClassLoader loader) { public boolean matches(ClassLoader loader) {
return getMismatchedReferenceSources(loader).isEmpty();
}
/**
* @param loader Classloader to validate against (or null for bootstrap)
* @return A list of all mismatches between this ReferenceMatcher and loader's classpath.
*/
public List<Reference.Mismatch> getMismatchedReferenceSources(ClassLoader loader) {
if (loader == BOOTSTRAP_LOADER) { if (loader == BOOTSTRAP_LOADER) {
loader = Utils.getBootstrapProxy(); loader = Utils.getBootstrapProxy();
} }
@ -62,8 +55,32 @@ public class ReferenceMatcher
} }
@Override @Override
public List<Mismatch> get(final ClassLoader loader) { public Boolean get(final ClassLoader loader) {
final List<Mismatch> mismatches = new ArrayList<>(0); for (final Reference reference : references) {
// Don't reference-check helper classes.
// They will be injected by the instrumentation's HelperInjector.
if (!helperClassNames.contains(reference.getClassName())) {
if (!checkMatch(reference, loader).isEmpty()) {
return false;
}
}
}
return true;
}
/**
* Loads the full list of mismatches. Used in debug contexts only
*
* @param loader Classloader to validate against (or null for bootstrap)
* @return A list of all mismatches between this ReferenceMatcher and loader's classpath.
*/
public List<Reference.Mismatch> getMismatchedReferenceSources(ClassLoader loader) {
if (loader == BOOTSTRAP_LOADER) {
loader = Utils.getBootstrapProxy();
}
final List<Mismatch> mismatches = new ArrayList<>();
for (final Reference reference : references) { for (final Reference reference : references) {
// Don't reference-check helper classes. // Don't reference-check helper classes.
@ -82,11 +99,12 @@ public class ReferenceMatcher
* @param loader * @param loader
* @return A list of mismatched sources. A list of size 0 means the reference matches the class. * @return A list of mismatched sources. A list of size 0 means the reference matches the class.
*/ */
public static List<Reference.Mismatch> checkMatch(Reference reference, ClassLoader loader) { private static List<Reference.Mismatch> checkMatch(
final Reference reference, final ClassLoader loader) {
final TypePool typePool = final TypePool typePool =
AgentTooling.poolStrategy() AgentTooling.poolStrategy()
.typePool(AgentTooling.locationStrategy().classFileLocator(loader), loader); .typePool(AgentTooling.locationStrategy().classFileLocator(loader), loader);
final List<Mismatch> mismatches = new ArrayList<>(0); final List<Mismatch> mismatches = new ArrayList<>();
try { try {
final TypePool.Resolution resolution = final TypePool.Resolution resolution =
typePool.describe(Utils.getClassName(reference.getClassName())); typePool.describe(Utils.getClassName(reference.getClassName()));
@ -96,7 +114,7 @@ public class ReferenceMatcher
reference.getSources().toArray(new Source[0]), reference.getClassName())); reference.getSources().toArray(new Source[0]), reference.getClassName()));
} }
return checkMatch(reference, resolution.resolve()); return checkMatch(reference, resolution.resolve());
} catch (Exception e) { } catch (final Exception e) {
if (e.getMessage().startsWith("Cannot resolve type description for ")) { if (e.getMessage().startsWith("Cannot resolve type description for ")) {
// bytebuddy throws an illegal state exception with this message if it cannot resolve types // bytebuddy throws an illegal state exception with this message if it cannot resolve types
// TODO: handle missing type resolutions without catching bytebuddy's exceptions // TODO: handle missing type resolutions without catching bytebuddy's exceptions
@ -112,10 +130,10 @@ public class ReferenceMatcher
} }
public static List<Reference.Mismatch> checkMatch( public static List<Reference.Mismatch> checkMatch(
Reference reference, TypeDescription typeOnClasspath) { final Reference reference, final TypeDescription typeOnClasspath) {
final List<Mismatch> mismatches = new ArrayList<>(0); final List<Mismatch> mismatches = new ArrayList<>();
for (Reference.Flag flag : reference.getFlags()) { for (final Reference.Flag flag : reference.getFlags()) {
if (!flag.matches(typeOnClasspath.getModifiers())) { if (!flag.matches(typeOnClasspath.getModifiers())) {
final String desc = reference.getClassName(); final String desc = reference.getClassName();
mismatches.add( mismatches.add(
@ -127,8 +145,8 @@ public class ReferenceMatcher
} }
} }
for (Reference.Field fieldRef : reference.getFields()) { for (final Reference.Field fieldRef : reference.getFields()) {
FieldDescription.InDefinedShape fieldDescription = findField(fieldRef, typeOnClasspath); final FieldDescription.InDefinedShape fieldDescription = findField(fieldRef, typeOnClasspath);
if (fieldDescription == null) { if (fieldDescription == null) {
mismatches.add( mismatches.add(
new Reference.Mismatch.MissingField( new Reference.Mismatch.MissingField(
@ -137,7 +155,7 @@ public class ReferenceMatcher
fieldRef.getName(), fieldRef.getName(),
fieldRef.getType().getInternalName())); fieldRef.getType().getInternalName()));
} else { } else {
for (Reference.Flag flag : fieldRef.getFlags()) { for (final Reference.Flag flag : fieldRef.getFlags()) {
if (!flag.matches(fieldDescription.getModifiers())) { if (!flag.matches(fieldDescription.getModifiers())) {
final String desc = final String desc =
reference.getClassName() reference.getClassName()
@ -155,7 +173,7 @@ public class ReferenceMatcher
} }
} }
for (Reference.Method methodRef : reference.getMethods()) { for (final Reference.Method methodRef : reference.getMethods()) {
final MethodDescription.InDefinedShape methodDescription = final MethodDescription.InDefinedShape methodDescription =
findMethod(methodRef, typeOnClasspath); findMethod(methodRef, typeOnClasspath);
if (methodDescription == null) { if (methodDescription == null) {
@ -165,7 +183,7 @@ public class ReferenceMatcher
methodRef.getName(), methodRef.getName(),
methodRef.getDescriptor())); methodRef.getDescriptor()));
} else { } else {
for (Reference.Flag flag : methodRef.getFlags()) { for (final Reference.Flag flag : methodRef.getFlags()) {
if (!flag.matches(methodDescription.getModifiers())) { if (!flag.matches(methodDescription.getModifiers())) {
final String desc = final String desc =
reference.getClassName() + "#" + methodRef.getName() + methodRef.getDescriptor(); reference.getClassName() + "#" + methodRef.getName() + methodRef.getDescriptor();
@ -184,8 +202,8 @@ public class ReferenceMatcher
} }
private static FieldDescription.InDefinedShape findField( private static FieldDescription.InDefinedShape findField(
Reference.Field fieldRef, TypeDescription typeOnClasspath) { final Reference.Field fieldRef, final TypeDescription typeOnClasspath) {
for (FieldDescription.InDefinedShape fieldType : typeOnClasspath.getDeclaredFields()) { for (final FieldDescription.InDefinedShape fieldType : typeOnClasspath.getDeclaredFields()) {
if (fieldType.getName().equals(fieldRef.getName()) if (fieldType.getName().equals(fieldRef.getName())
&& fieldType && fieldType
.getType() .getType()
@ -196,14 +214,14 @@ public class ReferenceMatcher
} }
} }
if (typeOnClasspath.getSuperClass() != null) { if (typeOnClasspath.getSuperClass() != null) {
FieldDescription.InDefinedShape fieldOnSupertype = final FieldDescription.InDefinedShape fieldOnSupertype =
findField(fieldRef, typeOnClasspath.getSuperClass().asErasure()); findField(fieldRef, typeOnClasspath.getSuperClass().asErasure());
if (fieldOnSupertype != null) { if (fieldOnSupertype != null) {
return fieldOnSupertype; return fieldOnSupertype;
} }
} }
for (TypeDescription.Generic interfaceType : typeOnClasspath.getInterfaces()) { for (final TypeDescription.Generic interfaceType : typeOnClasspath.getInterfaces()) {
FieldDescription.InDefinedShape fieldOnSupertype = final FieldDescription.InDefinedShape fieldOnSupertype =
findField(fieldRef, interfaceType.asErasure()); findField(fieldRef, interfaceType.asErasure());
if (fieldOnSupertype != null) { if (fieldOnSupertype != null) {
return fieldOnSupertype; return fieldOnSupertype;
@ -213,8 +231,8 @@ public class ReferenceMatcher
} }
private static MethodDescription.InDefinedShape findMethod( private static MethodDescription.InDefinedShape findMethod(
Reference.Method methodRef, TypeDescription typeOnClasspath) { final Reference.Method methodRef, final TypeDescription typeOnClasspath) {
for (MethodDescription.InDefinedShape methodDescription : for (final MethodDescription.InDefinedShape methodDescription :
typeOnClasspath.getDeclaredMethods()) { typeOnClasspath.getDeclaredMethods()) {
if (methodDescription.getInternalName().equals(methodRef.getName()) if (methodDescription.getInternalName().equals(methodRef.getName())
&& methodDescription.getDescriptor().equals(methodRef.getDescriptor())) { && methodDescription.getDescriptor().equals(methodRef.getDescriptor())) {
@ -222,14 +240,14 @@ public class ReferenceMatcher
} }
} }
if (typeOnClasspath.getSuperClass() != null) { if (typeOnClasspath.getSuperClass() != null) {
MethodDescription.InDefinedShape methodOnSupertype = final MethodDescription.InDefinedShape methodOnSupertype =
findMethod(methodRef, typeOnClasspath.getSuperClass().asErasure()); findMethod(methodRef, typeOnClasspath.getSuperClass().asErasure());
if (methodOnSupertype != null) { if (methodOnSupertype != null) {
return methodOnSupertype; return methodOnSupertype;
} }
} }
for (TypeDescription.Generic interfaceType : typeOnClasspath.getInterfaces()) { for (final TypeDescription.Generic interfaceType : typeOnClasspath.getInterfaces()) {
MethodDescription.InDefinedShape methodOnSupertype = final MethodDescription.InDefinedShape methodOnSupertype =
findMethod(methodRef, interfaceType.asErasure()); findMethod(methodRef, interfaceType.asErasure());
if (methodOnSupertype != null) { if (methodOnSupertype != null) {
return methodOnSupertype; return methodOnSupertype;

View File

@ -83,7 +83,6 @@ class HttpServerDecoratorTest extends ServerDecoratorTest {
then: then:
if (conn) { if (conn) {
1 * span.setTag(Tags.PEER_HOSTNAME.key, "test-host")
1 * span.setTag(Tags.PEER_PORT.key, 555) 1 * span.setTag(Tags.PEER_PORT.key, 555)
if (ipv4) { if (ipv4) {
1 * span.setTag(Tags.PEER_HOST_IPV4.key, "10.0.0.1") 1 * span.setTag(Tags.PEER_HOST_IPV4.key, "10.0.0.1")
@ -96,9 +95,9 @@ class HttpServerDecoratorTest extends ServerDecoratorTest {
where: where:
ipv4 | conn ipv4 | conn
null | null null | null
null | [host: "test-host", ip: null, port: 555] null | [ip: null, port: 555]
true | [host: "test-host", ip: "10.0.0.1", port: 555] true | [ip: "10.0.0.1", port: 555]
false | [host: "test-host", ip: "3ffe:1900:4545:3:200:f8ff:fe21:67cf", port: 555] false | [ip: "3ffe:1900:4545:3:200:f8ff:fe21:67cf", port: 555]
} }
def "test onResponse"() { def "test onResponse"() {
@ -173,11 +172,6 @@ class HttpServerDecoratorTest extends ServerDecoratorTest {
return m.url return m.url
} }
@Override
protected String peerHostname(Map m) {
return m.host
}
@Override @Override
protected String peerHostIP(Map m) { protected String peerHostIP(Map m) {
return m.ip return m.ip

View File

@ -33,6 +33,11 @@ class ClassLoaderMatcherTest extends DDSpecification {
!ClassLoaderMatcher.skipClassLoader().matches(null) !ClassLoaderMatcher.skipClassLoader().matches(null)
} }
def "DatadogClassLoader class name is hardcoded in ClassLoaderMatcher"() {
expect:
DatadogClassLoader.name == "datadog.trace.bootstrap.DatadogClassLoader"
}
/* /*
* A URLClassloader which only delegates java.* classes * A URLClassloader which only delegates java.* classes
*/ */

View File

@ -3,7 +3,7 @@ package datadog.trace.agent.test
import ch.qos.logback.classic.Level import ch.qos.logback.classic.Level
import ch.qos.logback.classic.Logger import ch.qos.logback.classic.Logger
import ch.qos.logback.core.read.ListAppender import ch.qos.logback.core.read.ListAppender
import datadog.trace.agent.tooling.ExceptionHandlers import datadog.trace.agent.tooling.bytebuddy.ExceptionHandlers
import datadog.trace.bootstrap.ExceptionLogger import datadog.trace.bootstrap.ExceptionLogger
import datadog.trace.util.test.DDSpecification import datadog.trace.util.test.DDSpecification
import net.bytebuddy.agent.ByteBuddyAgent import net.bytebuddy.agent.ByteBuddyAgent

View File

@ -1,6 +1,6 @@
package datadog.trace.agent.test package datadog.trace.agent.test
import datadog.trace.agent.tooling.DDLocationStrategy import datadog.trace.agent.tooling.bytebuddy.DDLocationStrategy
import datadog.trace.util.test.DDSpecification import datadog.trace.util.test.DDSpecification
import net.bytebuddy.agent.builder.AgentBuilder import net.bytebuddy.agent.builder.AgentBuilder
import spock.lang.Shared import spock.lang.Shared

View File

@ -1,5 +1,6 @@
package datadog.trace.agent.tooling package datadog.trace.agent.tooling
import datadog.trace.agent.tooling.bytebuddy.DDCachingPoolStrategy
import datadog.trace.util.test.DDSpecification import datadog.trace.util.test.DDSpecification
import net.bytebuddy.description.type.TypeDescription import net.bytebuddy.description.type.TypeDescription
import net.bytebuddy.dynamic.ClassFileLocator import net.bytebuddy.dynamic.ClassFileLocator
@ -204,7 +205,7 @@ class CacheProviderTest extends DDSpecification {
} }
static newClassLoader() { static newClassLoader() {
return new URLClassLoader([] as URL[], (ClassLoader)null) return new URLClassLoader([] as URL[], (ClassLoader) null)
} }
static newLocator() { static newLocator() {
@ -215,7 +216,8 @@ class CacheProviderTest extends DDSpecification {
} }
@Override @Override
void close() throws IOException {} void close() throws IOException {
}
} }
} }
} }

View File

@ -0,0 +1,52 @@
package datadog.trace.agent.tooling.bytebuddy.matcher
import datadog.trace.agent.tooling.bytebuddy.matcher.testclasses.A
import datadog.trace.agent.tooling.bytebuddy.matcher.testclasses.B
import datadog.trace.agent.tooling.bytebuddy.matcher.testclasses.F
import datadog.trace.agent.tooling.bytebuddy.matcher.testclasses.G
import datadog.trace.util.test.DDSpecification
import net.bytebuddy.description.type.TypeDescription
import net.bytebuddy.jar.asm.Opcodes
import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.extendsClass
import static net.bytebuddy.matcher.ElementMatchers.named
class ExtendsClassMatcherTest extends DDSpecification {
def "test matcher #matcherClass.simpleName -> #type.simpleName"() {
expect:
extendsClass(matcher).matches(argument) == result
where:
matcherClass | type | result
A | B | false
A | F | false
G | F | false
F | F | true
F | G | true
matcher = named(matcherClass.name)
argument = TypeDescription.ForLoadedType.of(type)
}
def "test traversal exceptions"() {
setup:
def type = Mock(TypeDescription)
def typeGeneric = Mock(TypeDescription.Generic)
def matcher = extendsClass(named(Object.name))
when:
def result = matcher.matches(type)
then:
!result // default to false
noExceptionThrown()
1 * type.getModifiers() >> Opcodes.ACC_ABSTRACT
1 * type.asGenericType() >> typeGeneric
1 * type.getTypeName() >> "type-name"
1 * typeGeneric.asErasure() >> { throw new Exception("asErasure exception") }
1 * typeGeneric.getTypeName() >> "typeGeneric-name"
1 * type.getSuperClass() >> { throw new Exception("getSuperClass exception") }
0 * _
}
}

View File

@ -0,0 +1,60 @@
package datadog.trace.agent.tooling.bytebuddy.matcher
import datadog.trace.agent.tooling.bytebuddy.matcher.testclasses.A
import datadog.trace.agent.tooling.bytebuddy.matcher.testclasses.B
import datadog.trace.agent.tooling.bytebuddy.matcher.testclasses.E
import datadog.trace.agent.tooling.bytebuddy.matcher.testclasses.F
import datadog.trace.agent.tooling.bytebuddy.matcher.testclasses.G
import datadog.trace.util.test.DDSpecification
import net.bytebuddy.description.type.TypeDescription
import net.bytebuddy.jar.asm.Opcodes
import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.hasInterface
import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.implementsInterface
import static net.bytebuddy.matcher.ElementMatchers.named
class HasInterfaceMatcherTest extends DDSpecification {
def "test matcher #matcherClass.simpleName -> #type.simpleName"() {
expect:
hasInterface(matcher).matches(argument) == result
where:
matcherClass | type | result
A | A | true
A | B | true
B | A | false
A | E | true
A | F | true
A | G | true
F | A | false
F | F | false
F | G | false
matcher = named(matcherClass.name)
argument = TypeDescription.ForLoadedType.of(type)
}
def "test traversal exceptions"() {
setup:
def type = Mock(TypeDescription)
def typeGeneric = Mock(TypeDescription.Generic)
def matcher = implementsInterface(named(Object.name))
when:
def result = matcher.matches(type)
then:
!result // default to false
noExceptionThrown()
1 * type.getModifiers() >> Opcodes.ACC_ABSTRACT
1 * type.isInterface() >> true
1 * type.asGenericType() >> typeGeneric
1 * typeGeneric.asErasure() >> { throw new Exception("asErasure exception") }
1 * typeGeneric.getTypeName() >> "typeGeneric-name"
1 * type.getInterfaces() >> { throw new Exception("getInterfaces exception") }
1 * type.getSuperClass() >> { throw new Exception("getSuperClass exception") }
2 * type.getTypeName() >> "type-name"
0 * _
}
}

View File

@ -0,0 +1,68 @@
package datadog.trace.agent.tooling.bytebuddy.matcher
import datadog.trace.agent.tooling.bytebuddy.matcher.testclasses.A
import datadog.trace.agent.tooling.bytebuddy.matcher.testclasses.B
import datadog.trace.agent.tooling.bytebuddy.matcher.testclasses.C
import datadog.trace.agent.tooling.bytebuddy.matcher.testclasses.F
import datadog.trace.agent.tooling.bytebuddy.matcher.testclasses.G
import datadog.trace.agent.tooling.bytebuddy.matcher.testclasses.TracedClass
import datadog.trace.agent.tooling.bytebuddy.matcher.testclasses.UntracedClass
import datadog.trace.api.Trace
import datadog.trace.util.test.DDSpecification
import net.bytebuddy.description.method.MethodDescription
import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.hasSuperMethod
import static net.bytebuddy.matcher.ElementMatchers.isAnnotatedWith
import static net.bytebuddy.matcher.ElementMatchers.none
class HasSuperMethodMatcherTest extends DDSpecification {
def "test matcher #type.simpleName #method"() {
expect:
hasSuperMethod(isAnnotatedWith(Trace)).matches(argument) == result
where:
type | method | result
A | "a" | false
B | "b" | true
C | "c" | false
F | "f" | true
G | "g" | false
TracedClass | "a" | true
UntracedClass | "a" | false
UntracedClass | "b" | true
argument = new MethodDescription.ForLoadedMethod(type.getDeclaredMethod(method))
}
def "test constructor never matches"() {
setup:
def method = Mock(MethodDescription)
def matcher = hasSuperMethod(none())
when:
def result = matcher.matches(method)
then:
!result
1 * method.isConstructor() >> true
0 * _
}
def "test traversal exceptions"() {
setup:
def method = Mock(MethodDescription)
def matcher = hasSuperMethod(none())
def sigToken = new MethodDescription.ForLoadedMethod(A.getDeclaredMethod("a")).asSignatureToken()
when:
def result = matcher.matches(method)
then:
!result // default to false
1 * method.isConstructor() >> false
1 * method.asSignatureToken() >> sigToken
1 * method.getDeclaringType() >> null
0 * _
}
}

View File

@ -0,0 +1,59 @@
package datadog.trace.agent.tooling.bytebuddy.matcher
import datadog.trace.agent.tooling.bytebuddy.matcher.testclasses.A
import datadog.trace.agent.tooling.bytebuddy.matcher.testclasses.B
import datadog.trace.agent.tooling.bytebuddy.matcher.testclasses.E
import datadog.trace.agent.tooling.bytebuddy.matcher.testclasses.F
import datadog.trace.agent.tooling.bytebuddy.matcher.testclasses.G
import datadog.trace.util.test.DDSpecification
import net.bytebuddy.description.type.TypeDescription
import net.bytebuddy.jar.asm.Opcodes
import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.implementsInterface
import static net.bytebuddy.matcher.ElementMatchers.named
class ImplementsInterfaceMatcherTest extends DDSpecification {
def "test matcher #matcherClass.simpleName -> #type.simpleName"() {
expect:
implementsInterface(matcher).matches(argument) == result
where:
matcherClass | type | result
A | A | false
A | B | false
B | A | false
A | E | false
A | F | true
A | G | true
F | A | false
F | F | false
F | G | false
matcher = named(matcherClass.name)
argument = TypeDescription.ForLoadedType.of(type)
}
def "test traversal exceptions"() {
setup:
def type = Mock(TypeDescription)
def typeGeneric = Mock(TypeDescription.Generic)
def matcher = implementsInterface(named(Object.name))
when:
def result = matcher.matches(type)
then:
!result // default to false
noExceptionThrown()
1 * type.getModifiers() >> Opcodes.ACC_ABSTRACT
1 * type.isInterface() >> true
1 * type.asGenericType() >> typeGeneric
1 * typeGeneric.asErasure() >> { throw new Exception("asErasure exception") }
1 * typeGeneric.getTypeName() >> "typeGeneric-name"
1 * type.getInterfaces() >> { throw new Exception("getInterfaces exception") }
1 * type.getSuperClass() >> { throw new Exception("getSuperClass exception") }
2 * type.getTypeName() >> "type-name"
0 * _
}
}

View File

@ -0,0 +1,58 @@
package datadog.trace.agent.tooling.bytebuddy.matcher
import datadog.trace.agent.tooling.bytebuddy.matcher.testclasses.A
import datadog.trace.agent.tooling.bytebuddy.matcher.testclasses.B
import datadog.trace.agent.tooling.bytebuddy.matcher.testclasses.E
import datadog.trace.agent.tooling.bytebuddy.matcher.testclasses.F
import datadog.trace.agent.tooling.bytebuddy.matcher.testclasses.G
import datadog.trace.util.test.DDSpecification
import net.bytebuddy.description.type.TypeDescription
import net.bytebuddy.jar.asm.Opcodes
import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.safeHasSuperType
import static net.bytebuddy.matcher.ElementMatchers.named
class SafeHasSuperTypeMatcherTest extends DDSpecification {
def "test matcher #matcherClass.simpleName -> #type.simpleName"() {
expect:
safeHasSuperType(matcher).matches(argument) == result
where:
matcherClass | type | result
A | A | false
A | B | false
B | A | false
A | E | false
A | F | true
B | G | true
F | A | false
F | F | true
F | G | true
matcher = named(matcherClass.name)
argument = TypeDescription.ForLoadedType.of(type)
}
def "test traversal exceptions"() {
setup:
def type = Mock(TypeDescription)
def typeGeneric = Mock(TypeDescription.Generic)
def matcher = safeHasSuperType(named(Object.name))
when:
def result = matcher.matches(type)
then:
!result // default to false
noExceptionThrown()
1 * type.getModifiers() >> Opcodes.ACC_ABSTRACT
1 * type.asGenericType() >> typeGeneric
1 * typeGeneric.asErasure() >> { throw new Exception("asErasure exception") }
1 * typeGeneric.getTypeName() >> "typeGeneric-name"
1 * type.getInterfaces() >> { throw new Exception("getInterfaces exception") }
1 * type.getSuperClass() >> { throw new Exception("getSuperClass exception") }
2 * type.getTypeName() >> "type-name"
0 * _
}
}

View File

@ -0,0 +1,40 @@
package datadog.trace.agent.tooling.bytebuddy.matcher
import datadog.trace.util.test.DDSpecification
import net.bytebuddy.matcher.ElementMatcher
import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.failSafe
class SafeMatcherTest extends DDSpecification {
def mockMatcher = Mock(ElementMatcher)
def "test matcher"() {
setup:
def matcher = failSafe(mockMatcher, "test")
when:
def result = matcher.matches(new Object())
then:
1 * mockMatcher.matches(_) >> match
result == match
where:
match << [true, false]
}
def "test matcher exception"() {
setup:
def matcher = failSafe(mockMatcher, "test")
when:
def result = matcher.matches(new Object())
then:
1 * mockMatcher.matches(_) >> { throw new Exception("matcher exception") }
0 * _
noExceptionThrown()
!result // default to false
}
}

View File

@ -0,0 +1,5 @@
package datadog.trace.agent.tooling.bytebuddy.matcher.testclasses;
public interface A {
void a();
}

View File

@ -0,0 +1,8 @@
package datadog.trace.agent.tooling.bytebuddy.matcher.testclasses;
import datadog.trace.api.Trace;
public interface B extends A {
@Trace
void b();
}

View File

@ -0,0 +1,5 @@
package datadog.trace.agent.tooling.bytebuddy.matcher.testclasses;
public interface C extends A, B {
void c();
}

View File

@ -0,0 +1,8 @@
package datadog.trace.agent.tooling.bytebuddy.matcher.testclasses;
import datadog.trace.api.Trace;
public interface D extends A, B, C {
@Trace
void d();
}

View File

@ -0,0 +1,5 @@
package datadog.trace.agent.tooling.bytebuddy.matcher.testclasses;
public interface E extends B, C, D {
void e();
}

View File

@ -0,0 +1,8 @@
package datadog.trace.agent.tooling.bytebuddy.matcher.testclasses;
import datadog.trace.api.Trace;
public abstract class F implements E {
@Trace
public abstract void f();
}

View File

@ -0,0 +1,5 @@
package datadog.trace.agent.tooling.bytebuddy.matcher.testclasses;
public abstract class G extends F {
public void g() {}
}

View File

@ -0,0 +1,33 @@
package datadog.trace.agent.tooling.bytebuddy.matcher.testclasses;
import datadog.trace.api.Trace;
public class TracedClass extends UntracedClass {
@Trace
@Override
public void g() {}
@Trace
@Override
public void f() {}
@Trace
@Override
public void e() {}
@Trace
@Override
public void d() {}
@Trace
@Override
public void c() {}
@Trace
@Override
public void b() {}
@Trace
@Override
public void a() {}
}

View File

@ -0,0 +1,24 @@
package datadog.trace.agent.tooling.bytebuddy.matcher.testclasses;
public class UntracedClass extends G {
@Override
public void g() {}
@Override
public void f() {}
@Override
public void e() {}
@Override
public void d() {}
@Override
public void c() {}
@Override
public void b() {}
@Override
public void a() {}
}

View File

@ -6,68 +6,44 @@ apply from: "${rootDir}/gradle/java.gradle"
dependencies { dependencies {
jmh project(':dd-trace-api') jmh project(':dd-trace-api')
jmh group: 'net.bytebuddy', name: 'byte-buddy-agent', version: '1.7.6' jmh deps.bytebuddyagent
// Add a bunch of dependencies so instrumentation is not disabled.
jmh group: 'javax.jms', name: 'javax.jms-api', version: '2.0.1'
jmh group: 'javax.servlet', name: 'javax.servlet-api', version: '3.0.1'
jmh group: 'org.mongodb', name: 'mongo-java-driver', version: '3.4.2'
jmh group: 'org.mongodb', name: 'mongodb-driver-async', version: '3.4.2'
jmh(group: 'com.amazonaws', name: 'aws-java-sdk', version: '1.11.119') {
exclude(module: 'httpclient')
exclude(module: 'jackson-databind')
exclude(module: 'jackson-dataformat-cbor')
}
jmh group: 'com.squareup.okhttp3', name: 'okhttp', version: '3.6.0'
jmh group: 'org.apache.httpcomponents', name: 'httpclient', version: '4.5.3'
jmh(group: 'com.datastax.cassandra', name: 'cassandra-driver-core', version: '3.2.0')
} }
configurations.testRuntimeClasspath.dependencies.clear()
jmh { jmh {
timeUnit = 'us' // Output time unit. Available time units are: [m, s, ms, us, ns]. timeUnit = 'ms' // Output time unit. Available time units are: [m, s, ms, us, ns].
benchmarkMode = ['thrpt', 'avgt'] benchmarkMode = ['avgt']
// timeOnIteration = '5s' timeOnIteration = '20s'
iterations = 5 // Number of measurement iterations to do. iterations = 1 // Number of measurement iterations to do.
fork = 1 // How many times to forks a single benchmark. Use 0 to disable forking altogether fork = 1 // How many times to forks a single benchmark. Use 0 to disable forking altogether
// jvmArgs = ["-Dasdf=123"] jvmArgs = ["-Ddd.jmxfetch.enabled=false", "-Ddd.writer.type=LoggingWriter"]
// jvmArgs = ["-javaagent:${project(':dd-java-agent').shadowJar.archivePath}"] // jvmArgs += ["-XX:+UnlockDiagnosticVMOptions", "-XX:+DebugNonSafepoints", "-XX:StartFlightRecording=delay=5s,dumponexit=true,name=jmh-benchmark,filename=${rootDir}/dd-java-agent/benchmark/build/reports/jmh/jmh-benchmark.jfr"]
// jvmArgs += ["-agentpath:${rootDir}/dd-java-agent/benchmark/src/jmh/resources/libasyncProfiler.so=start,collapsed,file=${rootDir}/dd-java-agent/benchmark/build/reports/jmh/profiler.txt".toString()]
failOnError = true // Should JMH fail immediately if any benchmark had experienced the unrecoverable error? failOnError = true // Should JMH fail immediately if any benchmark had experienced the unrecoverable error?
// warmup = '2s' // Time to spend at each warmup iteration. warmup = '5s' // Time to spend at each warmup iteration.
// warmupIterations = 2 // Number of warmup iterations to do. // warmupBatchSize = 10 // Warmup batch size: number of benchmark method calls per operation.
// warmupForks = 0 // How many warmup forks to make for a single benchmark. 0 to disable warmup forks. warmupForks = 0 // How many warmup forks to make for a single benchmark. 0 to disable warmup forks.
warmupIterations = 1 // Number of warmup iterations to do.
// profilers = ['stack'] // profilers = ['stack:lines=5;detailLine=true;period=5;excludePackages=true']
// Use profilers to collect additional data. Supported profilers: [cl, comp, gc, stack, perf, perfnorm, perfasm, xperf, xperfasm, hs_cl, hs_comp, hs_gc, hs_rt, hs_thr] // Use profilers to collect additional data. Supported profilers: [cl, comp, gc, stack, perf, perfnorm, perfasm, xperf, xperfasm, hs_cl, hs_comp, hs_gc, hs_rt, hs_thr]
// humanOutputFile = project.file("${project.buildDir}/reports/jmh/human.txt") // human-readable output file // humanOutputFile = project.file("${project.buildDir}/reports/jmh/human.txt") // human-readable output file
// operationsPerInvocation = 10 // Operations per invocation. // operationsPerInvocation = 10 // Operations per invocation.
// synchronizeIterations = false // Synchronize iterations? // synchronizeIterations = false // Synchronize iterations?
timeout = '1s' // Timeout for benchmark iteration. timeout = '5s' // Timeout for benchmark iteration.
includeTests = false // includeTests = false
// Allows to include test sources into generate JMH jar, i.e. use it when benchmarks depend on the test classes. // Allows to include test sources into generate JMH jar, i.e. use it when benchmarks depend on the test classes.
duplicateClassesStrategy = 'fail' duplicateClassesStrategy = DuplicatesStrategy.EXCLUDE
jmhVersion = '1.20' // Specifies JMH version jmhVersion = '1.23' // Specifies JMH version
} }
// configured as a separate task since the 'jmh' task did not like adding a javaagent argument. tasks.jmh.dependsOn project(':dd-java-agent').shadowJar
tasks.register("jmhAgent", JavaExec) {
classpath = files(project.jmhCompileGeneratedClasses.destinationDir)
classpath += sourceSets.jmh.runtimeClasspath
main = "org.openjdk.jmh.Main"
args += ["-tu", "us"]
args += ["-bm", "avgt"]
// args += ["-prof", "stack:lines=5;detailLine=true;period=5;excludePackages=true"]
args += ["-f", "1"]
args += ["-foe", "true"]
// args += ["-wi", "2"] /*
// args += ["-i", "5"] If using libasyncProfiler, use the following to generate nice svg flamegraphs.
dependsOn project.tasks.jmhCompileGeneratedClasses sed '/unknown/d' dd-java-agent/benchmark/build/reports/jmh/profiler.txt | sed '/^thread_start/d' | sed '/not_walkable/d' > dd-java-agent/benchmark/build/reports/jmh/profiler-cleaned.txt
} (using https://github.com/brendangregg/FlameGraph)
./flamegraph.pl --color=java dd-java-agent/benchmark/build/reports/jmh/profiler-cleaned.txt > dd-java-agent/benchmark/build/reports/jmh/jmh-master.svg
tasks.jmhAgent.dependsOn project(':dd-java-agent').shadowJar */

View File

@ -4,49 +4,17 @@ import datadog.benchmark.classes.TracedClass;
import datadog.benchmark.classes.UntracedClass; import datadog.benchmark.classes.UntracedClass;
import java.lang.instrument.Instrumentation; import java.lang.instrument.Instrumentation;
import java.lang.instrument.UnmodifiableClassException; import java.lang.instrument.UnmodifiableClassException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.nio.file.Paths;
import net.bytebuddy.agent.ByteBuddyAgent; import net.bytebuddy.agent.ByteBuddyAgent;
import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.Fork; import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Scope; import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State; import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.TearDown;
public class ClassRetransformingBenchmark { public class ClassRetransformingBenchmark {
public static final String BENCHMARK_HOME =
Paths.get(".").toAbsolutePath().normalize().toString();
static {
if (!BENCHMARK_HOME.endsWith("benchmark")) {
throw new IllegalArgumentException("Invalid Home directory: " + BENCHMARK_HOME);
}
}
@State(Scope.Benchmark) @State(Scope.Benchmark)
public static class BenchmarkState { public static class BenchmarkState {
private final Instrumentation inst = ByteBuddyAgent.install(); private final Instrumentation inst = ByteBuddyAgent.install();
@Setup
public void initializeInstrumentation() {
// loading TracedClass will initialize helper injection
TracedClass.class.getName();
}
@TearDown
public void stopAgent() {
try {
final Class<?> gt = Class.forName("io.opentracing.util.GlobalTracer");
final Field tracerField = gt.getDeclaredField("tracer");
tracerField.setAccessible(true);
final Object tracer = tracerField.get(null);
final Method close = tracer.getClass().getMethod("close");
close.invoke(tracer);
} catch (final Exception e) {
}
}
} }
@Benchmark @Benchmark
@ -60,54 +28,11 @@ public class ClassRetransformingBenchmark {
state.inst.retransformClasses(TracedClass.class); state.inst.retransformClasses(TracedClass.class);
} }
@Fork(jvmArgsAppend = "-javaagent:releases/dd-java-agent-0.2.2.jar") @Fork(jvmArgsAppend = "-javaagent:/path/to/dd-java-agent-master.jar")
public static class WithAgent022 extends ClassRetransformingBenchmark {} public static class WithAgentMaster extends ClassRetransformingBenchmark {}
@Fork(jvmArgsAppend = "-javaagent:releases/dd-java-agent-0.2.4.jar") @Fork(
public static class WithAgent024 extends ClassRetransformingBenchmark {} jvmArgsAppend =
"-javaagent:/path/to/dd-trace-java/dd-java-agent/build/libs/dd-java-agent.jar")
@Fork(jvmArgsAppend = "-javaagent:releases/dd-java-agent-0.2.6.jar")
public static class WithAgent026 extends ClassRetransformingBenchmark {}
@Fork(jvmArgsAppend = "-javaagent:releases/dd-java-agent-0.2.7.jar")
public static class WithAgent027 extends ClassRetransformingBenchmark {}
@Fork(jvmArgsAppend = "-javaagent:releases/dd-java-agent-0.2.8.jar")
public static class WithAgent028 extends ClassRetransformingBenchmark {}
@Fork(jvmArgsAppend = "-javaagent:releases/dd-java-agent-0.2.9.jar")
public static class WithAgent029 extends ClassRetransformingBenchmark {}
@Fork(jvmArgsAppend = "-javaagent:releases/dd-java-agent-0.2.10.jar")
public static class WithAgent0210 extends ClassRetransformingBenchmark {}
@Fork(jvmArgsAppend = "-javaagent:releases/dd-java-agent-0.2.11.jar")
public static class WithAgent0211 extends ClassRetransformingBenchmark {}
@Fork(jvmArgsAppend = "-javaagent:releases/dd-java-agent-0.2.12.jar")
public static class WithAgent0212 extends ClassRetransformingBenchmark {}
@Fork(jvmArgsAppend = "-javaagent:releases/dd-java-agent-0.3.0.jar")
public static class WithAgent030 extends ClassRetransformingBenchmark {}
@Fork(jvmArgsAppend = "-javaagent:releases/dd-java-agent-0.3.1.jar")
public static class WithAgent031 extends ClassRetransformingBenchmark {}
@Fork(jvmArgsAppend = "-javaagent:releases/dd-java-agent-0.3.2.jar")
public static class WithAgent032 extends ClassRetransformingBenchmark {}
@Fork(jvmArgsAppend = "-javaagent:releases/dd-java-agent-0.3.3.jar")
public static class WithAgent033 extends ClassRetransformingBenchmark {}
@Fork(jvmArgsAppend = "-javaagent:releases/dd-java-agent-0.4.0.jar")
public static class WithAgent040 extends ClassRetransformingBenchmark {}
@Fork(jvmArgsAppend = "-javaagent:releases/dd-java-agent-0.4.1.jar")
public static class WithAgent041 extends ClassRetransformingBenchmark {}
@Fork(jvmArgsAppend = "-javaagent:releases/dd-java-agent-0.5.0.jar")
public static class WithAgent050 extends ClassRetransformingBenchmark {}
@Fork(jvmArgsAppend = "-javaagent:../build/libs/dd-java-agent.jar")
public static class WithAgent extends ClassRetransformingBenchmark {} public static class WithAgent extends ClassRetransformingBenchmark {}
} }

View File

@ -1,5 +1,5 @@
package datadog.benchmark.classes; package datadog.benchmark.classes;
public interface C extends B { public interface C extends A, B {
void c(); void c();
} }

View File

@ -1,5 +1,5 @@
package datadog.benchmark.classes; package datadog.benchmark.classes;
public interface D extends C { public interface D extends A, B, C {
void d(); void d();
} }

View File

@ -1,5 +1,5 @@
package datadog.benchmark.classes; package datadog.benchmark.classes;
public interface E extends D { public interface E extends B, C, D {
void e(); void e();
} }

View File

@ -1 +0,0 @@
enableCustomAnnotationTracingOver: ["datadog.benchmark"]

View File

@ -30,11 +30,6 @@ public class AkkaHttpServerDecorator
return new URI(httpRequest.uri().toString()); return new URI(httpRequest.uri().toString());
} }
@Override
protected String peerHostname(final HttpRequest httpRequest) {
return null;
}
@Override @Override
protected String peerHostIP(final HttpRequest httpRequest) { protected String peerHostIP(final HttpRequest httpRequest) {
return null; return null;

View File

@ -1,6 +1,6 @@
package datadog.trace.instrumentation.apachehttpasyncclient; package datadog.trace.instrumentation.apachehttpasyncclient;
import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.safeHasSuperType; import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.implementsInterface;
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activeScope; import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activeScope;
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.propagate; import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.propagate;
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan; import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan;
@ -40,7 +40,7 @@ public class ApacheHttpAsyncClientInstrumentation extends Instrumenter.Default {
@Override @Override
public ElementMatcher<TypeDescription> typeMatcher() { public ElementMatcher<TypeDescription> typeMatcher() {
return safeHasSuperType(named("org.apache.http.nio.client.HttpAsyncClient")); return implementsInterface(named("org.apache.http.nio.client.HttpAsyncClient"));
} }
@Override @Override

View File

@ -1,6 +1,6 @@
package datadog.trace.instrumentation.apachehttpasyncclient; package datadog.trace.instrumentation.apachehttpasyncclient;
import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.safeHasSuperType; import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.implementsInterface;
import static java.util.Collections.singletonMap; import static java.util.Collections.singletonMap;
import static net.bytebuddy.matcher.ElementMatchers.isMethod; import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.named;
@ -31,7 +31,7 @@ public class ApacheHttpClientRedirectInstrumentation extends Instrumenter.Defaul
@Override @Override
public ElementMatcher<TypeDescription> typeMatcher() { public ElementMatcher<TypeDescription> typeMatcher() {
return safeHasSuperType(named("org.apache.http.client.RedirectStrategy")); return implementsInterface(named("org.apache.http.client.RedirectStrategy"));
} }
@Override @Override

View File

@ -1,13 +1,12 @@
package datadog.trace.instrumentation.apachehttpclient; package datadog.trace.instrumentation.apachehttpclient;
import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.safeHasSuperType; import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.implementsInterface;
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activateSpan; import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activateSpan;
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.propagate; import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.propagate;
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan; import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan;
import static datadog.trace.instrumentation.apachehttpclient.ApacheHttpClientDecorator.DECORATE; import static datadog.trace.instrumentation.apachehttpclient.ApacheHttpClientDecorator.DECORATE;
import static datadog.trace.instrumentation.apachehttpclient.HttpHeadersInjectAdapter.SETTER; import static datadog.trace.instrumentation.apachehttpclient.HttpHeadersInjectAdapter.SETTER;
import static net.bytebuddy.matcher.ElementMatchers.isAbstract; import static net.bytebuddy.matcher.ElementMatchers.isAbstract;
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
import static net.bytebuddy.matcher.ElementMatchers.isMethod; import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.not; import static net.bytebuddy.matcher.ElementMatchers.not;
@ -44,7 +43,7 @@ public class ApacheHttpClientInstrumentation extends Instrumenter.Default {
@Override @Override
public ElementMatcher<TypeDescription> typeMatcher() { public ElementMatcher<TypeDescription> typeMatcher() {
return not(isInterface()).and(safeHasSuperType(named("org.apache.http.client.HttpClient"))); return implementsInterface(named("org.apache.http.client.HttpClient"));
} }
@Override @Override

View File

@ -9,7 +9,6 @@ import datadog.trace.api.DDTags;
import datadog.trace.bootstrap.ContextStore; import datadog.trace.bootstrap.ContextStore;
import datadog.trace.bootstrap.instrumentation.api.AgentSpan; import datadog.trace.bootstrap.instrumentation.api.AgentSpan;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
@ -102,8 +101,8 @@ public class AwsSdkClientDecorator extends HttpClientDecorator<Request, Response
} }
@Override @Override
protected URI url(final Request request) throws URISyntaxException { protected URI url(final Request request) {
return new URI(request.getEndpoint().toString()); return request.getEndpoint();
} }
@Override @Override

View File

@ -1,7 +1,8 @@
package datadog.trace.instrumentation.aws.v0; package datadog.trace.instrumentation.aws.v0;
import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.safeHasSuperType; import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.extendsClass;
import static java.util.Collections.singletonMap; import static java.util.Collections.singletonMap;
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.takesArgument; import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
@ -26,7 +27,8 @@ public final class RequestInstrumentation extends Instrumenter.Default {
@Override @Override
public ElementMatcher<TypeDescription> typeMatcher() { public ElementMatcher<TypeDescription> typeMatcher() {
return safeHasSuperType(named("com.amazonaws.AmazonWebServiceRequest")); return nameStartsWith("com.amazonaws.")
.and(extendsClass(named("com.amazonaws.AmazonWebServiceRequest")));
} }
@Override @Override

View File

@ -1,12 +1,11 @@
package datadog.trace.instrumentation.aws.v2; package datadog.trace.instrumentation.aws.v2;
import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.safeHasSuperType; import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.implementsInterface;
import static java.util.Collections.singletonMap; import static java.util.Collections.singletonMap;
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
import static net.bytebuddy.matcher.ElementMatchers.isMethod; import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.isPublic; import static net.bytebuddy.matcher.ElementMatchers.isPublic;
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.not;
import com.google.auto.service.AutoService; import com.google.auto.service.AutoService;
import datadog.trace.agent.tooling.Instrumenter; import datadog.trace.agent.tooling.Instrumenter;
@ -23,8 +22,10 @@ public final class AwsClientInstrumentation extends AbstractAwsClientInstrumenta
@Override @Override
public ElementMatcher<TypeDescription> typeMatcher() { public ElementMatcher<TypeDescription> typeMatcher() {
return safeHasSuperType(named("software.amazon.awssdk.core.client.builder.SdkClientBuilder")) return nameStartsWith("software.amazon.awssdk.")
.and(not(isInterface())); .and(
implementsInterface(
named("software.amazon.awssdk.core.client.builder.SdkClientBuilder")));
} }
@Override @Override

View File

@ -1,12 +1,11 @@
package datadog.trace.instrumentation.aws.v2; package datadog.trace.instrumentation.aws.v2;
import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.safeHasSuperType; import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.extendsClass;
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activeScope; import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activeScope;
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
import static net.bytebuddy.matcher.ElementMatchers.isMethod; import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.isPublic; import static net.bytebuddy.matcher.ElementMatchers.isPublic;
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.not;
import com.google.auto.service.AutoService; import com.google.auto.service.AutoService;
import datadog.trace.agent.tooling.Instrumenter; import datadog.trace.agent.tooling.Instrumenter;
@ -28,12 +27,14 @@ public final class AwsHttpClientInstrumentation extends AbstractAwsClientInstrum
@Override @Override
public ElementMatcher<TypeDescription> typeMatcher() { public ElementMatcher<TypeDescription> typeMatcher() {
return safeHasSuperType( return nameStartsWith("software.amazon.awssdk.")
named("software.amazon.awssdk.core.internal.http.pipeline.stages.MakeHttpRequestStage") .and(
.or( extendsClass(
named( named(
"software.amazon.awssdk.core.internal.http.pipeline.stages.MakeAsyncHttpRequestStage"))) "software.amazon.awssdk.core.internal.http.pipeline.stages.MakeHttpRequestStage")
.and(not(isInterface())); .or(
named(
"software.amazon.awssdk.core.internal.http.pipeline.stages.MakeAsyncHttpRequestStage"))));
} }
@Override @Override

View File

@ -1,5 +1,4 @@
import datadog.trace.agent.test.AgentTestRunner import datadog.trace.agent.test.AgentTestRunner
import datadog.trace.instrumentation.TestBean
import org.jboss.weld.environment.se.Weld import org.jboss.weld.environment.se.Weld
import org.jboss.weld.environment.se.WeldContainer import org.jboss.weld.environment.se.WeldContainer
import org.jboss.weld.environment.se.threading.RunnableDecorator import org.jboss.weld.environment.se.threading.RunnableDecorator

View File

@ -1,5 +1,3 @@
package datadog.trace.instrumentation;
public class TestBean { public class TestBean {
private String someField; private String someField;

View File

@ -0,0 +1 @@
apply from: "${rootDir}/gradle/java.gradle"

View File

@ -1,5 +1,7 @@
apply from: "${rootDir}/gradle/java.gradle" apply from: "${rootDir}/gradle/java.gradle"
dependencies { dependencies {
testCompile project(':dd-java-agent:instrumentation:classloading')
testCompile group: 'org.jboss.modules', name: 'jboss-modules', version: '1.3.10.Final' testCompile group: 'org.jboss.modules', name: 'jboss-modules', version: '1.3.10.Final'
} }

View File

@ -0,0 +1,34 @@
import datadog.trace.agent.test.AgentTestRunner
import org.jboss.modules.ModuleFinder
import org.jboss.modules.ModuleIdentifier
import org.jboss.modules.ModuleLoadException
import org.jboss.modules.ModuleLoader
import org.jboss.modules.ModuleSpec
class JBossClassloadingTest extends AgentTestRunner {
def "delegates to bootstrap class loader for agent classes"() {
setup:
def moduleFinders = new ModuleFinder[1]
moduleFinders[0] = new ModuleFinder() {
@Override
ModuleSpec findModule(ModuleIdentifier identifier, ModuleLoader delegateLoader) throws ModuleLoadException {
return ModuleSpec.build(identifier).create()
}
}
def moduleLoader = new ModuleLoader(moduleFinders)
def moduleId = ModuleIdentifier.fromString("test")
def testModule = moduleLoader.loadModule(moduleId)
def classLoader = testModule.getClassLoader()
when:
Class<?> clazz
try {
clazz = Class.forName("datadog.trace.api.GlobalTracer", false, classLoader)
} catch (ClassNotFoundException e) {
}
then:
assert clazz != null
assert clazz.getClassLoader() == null
}
}

View File

@ -1,6 +1,7 @@
apply from: "${rootDir}/gradle/java.gradle" apply from: "${rootDir}/gradle/java.gradle"
dependencies { dependencies {
testCompile project(':dd-java-agent:instrumentation:classloading')
// TODO: we should separate core and Eclipse tests at some point, // TODO: we should separate core and Eclipse tests at some point,
// but right now core-specific tests are quite dump and are run with // but right now core-specific tests are quite dump and are run with

View File

@ -0,0 +1,68 @@
import datadog.trace.agent.test.AgentTestRunner
import org.apache.felix.framework.BundleWiringImpl
import org.eclipse.osgi.internal.debug.Debug
import org.eclipse.osgi.internal.framework.EquinoxConfiguration
import org.eclipse.osgi.internal.loader.BundleLoader
import org.eclipse.osgi.internal.loader.ModuleClassLoader
import org.eclipse.osgi.internal.loader.classpath.ClasspathManager
import org.eclipse.osgi.storage.BundleInfo
class OSGIClassloadingTest extends AgentTestRunner {
def "OSGI delegates to bootstrap class loader for agent classes"() {
when:
def clazz
if (args == 1) {
clazz = loader.loadClass("datadog.trace.api.GlobalTracer")
} else {
clazz = loader.loadClass("datadog.trace.api.GlobalTracer", false)
}
then:
assert clazz != null
assert clazz.getClassLoader() == null
where:
loader | args
new TestClassLoader() | 1
new TestClassLoader() | 2
new BundleWiringImpl.BundleClassLoader(null, null, null) | 1
new BundleWiringImpl.BundleClassLoader(null, null, null) | 2
}
static class TestClassLoader extends ModuleClassLoader {
TestClassLoader() {
super(null)
}
@Override
protected BundleInfo.Generation getGeneration() {
return null
}
@Override
protected Debug getDebug() {
return null
}
@Override
ClasspathManager getClasspathManager() {
return null
}
@Override
protected EquinoxConfiguration getConfiguration() {
return null
}
@Override
BundleLoader getBundleLoader() {
return null
}
@Override
boolean isRegisteredAsParallel() {
return false
}
}
}

View File

@ -0,0 +1,105 @@
package datadog.trace.instrumentation.classloading;
import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.extendsClass;
import static java.util.Collections.singletonMap;
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.isProtected;
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
import static net.bytebuddy.matcher.ElementMatchers.isStatic;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.not;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
import com.google.auto.service.AutoService;
import datadog.trace.agent.tooling.Constants;
import datadog.trace.agent.tooling.Instrumenter;
import datadog.trace.bootstrap.CallDepthThreadLocalMap;
import java.util.Map;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
/*
* Some class loaders do not delegate to their parent, so classes in those class loaders
* will not be able to see classes in the bootstrap class loader.
*
* In particular, instrumentation on classes in those class loaders will not be able to see
* the shaded OpenTelemetry API classes in the bootstrap class loader.
*
* This instrumentation forces all class loaders to delegate to the bootstrap class loader
* for the classes that we have put in the bootstrap class loader.
*/
@AutoService(Instrumenter.class)
public final class ClassloadingInstrumentation extends Instrumenter.Default {
public ClassloadingInstrumentation() {
super("classloading");
}
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
// just an optimization to exclude common class loaders that are known to delegate to the
// bootstrap loader (or happen to _be_ the bootstrap loader)
return not(named("java.lang.ClassLoader"))
.and(not(named("com.ibm.oti.vm.BootstrapClassLoader")))
.and(not(named("datadog.trace.bootstrap.AgentClassLoader")))
.and(extendsClass(named("java.lang.ClassLoader")));
}
@Override
public String[] helperClassNames() {
return new String[] {Constants.class.getName()};
}
@Override
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
return singletonMap(
isMethod()
.and(named("loadClass"))
.and(
takesArguments(1)
.and(takesArgument(0, named("java.lang.String")))
.or(
takesArguments(2)
.and(takesArgument(0, named("java.lang.String")))
.and(takesArgument(1, named("boolean")))))
.and(isPublic().or(isProtected()))
.and(not(isStatic())),
ClassloadingInstrumentation.class.getName() + "$LoadClassAdvice");
}
public static class LoadClassAdvice {
@Advice.OnMethodEnter(skipOn = Advice.OnNonDefaultValue.class)
public static Class<?> onEnter(
@Advice.Argument(0) final String name, @Advice.Local("_callDepth") int callDepth) {
callDepth = CallDepthThreadLocalMap.incrementCallDepth(ClassLoader.class);
if (callDepth > 0) {
return null;
}
for (final String prefix : Constants.BOOTSTRAP_PACKAGE_PREFIXES) {
if (name.startsWith(prefix)) {
try {
return Class.forName(name, false, null);
} catch (final ClassNotFoundException e) {
}
}
}
return null;
}
@Advice.OnMethodExit(onThrowable = Throwable.class)
public static void onExit(
@Advice.Local("_callDepth") final int callDepth,
@Advice.Return(readOnly = false) Class<?> result,
@Advice.Enter final Class<?> resultFromBootstrapLoader) {
if (callDepth > 0) {
return;
}
CallDepthThreadLocalMap.reset(ClassLoader.class);
if (resultFromBootstrapLoader != null) {
result = resultFromBootstrapLoader;
}
}
}
}

View File

@ -0,0 +1,39 @@
import datadog.trace.agent.test.AgentTestRunner
class ClassloadingTest extends AgentTestRunner {
def "delegates to bootstrap class loader for agent classes"() {
setup:
def classLoader = new NonDelegatingURLClassLoader()
when:
Class<?> clazz
try {
clazz = Class.forName("datadog.trace.api.GlobalTracer", false, classLoader)
} catch (ClassNotFoundException e) {
}
then:
assert clazz != null
assert clazz.getClassLoader() == null
}
static class NonDelegatingURLClassLoader extends URLClassLoader {
NonDelegatingURLClassLoader() {
super(new URL[0])
}
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
synchronized (getClassLoadingLock(name)) {
Class<?> clazz = findLoadedClass(name)
if (clazz == null) {
clazz = findClass(name)
}
if (resolve) {
resolveClass(clazz)
}
return clazz
}
}
}
}

View File

@ -18,6 +18,8 @@ testSets {
} }
dependencies { dependencies {
testCompile project(':dd-java-agent:instrumentation:classloading')
//This seems to be the earliest version that has org.apache.catalina.loader.WebappClassLoaderBase //This seems to be the earliest version that has org.apache.catalina.loader.WebappClassLoaderBase
//Older versions would require slightly different instrumentation. //Older versions would require slightly different instrumentation.
testCompile group: 'org.apache.tomcat', name: 'tomcat-catalina', version: '8.0.14' testCompile group: 'org.apache.tomcat', name: 'tomcat-catalina', version: '8.0.14'

View File

@ -1,11 +1,9 @@
package datadog.trace.instrumentation.couchbase.client; package datadog.trace.instrumentation.couchbase.client;
import static java.util.Collections.singletonMap; import static java.util.Collections.singletonMap;
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
import static net.bytebuddy.matcher.ElementMatchers.isMethod; import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.isPublic; import static net.bytebuddy.matcher.ElementMatchers.isPublic;
import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.not;
import static net.bytebuddy.matcher.ElementMatchers.returns; import static net.bytebuddy.matcher.ElementMatchers.returns;
import com.couchbase.client.java.CouchbaseCluster; import com.couchbase.client.java.CouchbaseCluster;
@ -29,10 +27,8 @@ public class CouchbaseBucketInstrumentation extends Instrumenter.Default {
@Override @Override
public ElementMatcher<TypeDescription> typeMatcher() { public ElementMatcher<TypeDescription> typeMatcher() {
return not(isInterface()) return named("com.couchbase.client.java.bucket.DefaultAsyncBucketManager")
.and( .or(named("com.couchbase.client.java.CouchbaseAsyncBucket"));
named("com.couchbase.client.java.bucket.DefaultAsyncBucketManager")
.or(named("com.couchbase.client.java.CouchbaseAsyncBucket")));
} }
@Override @Override
@ -64,7 +60,7 @@ public class CouchbaseBucketInstrumentation extends Instrumenter.Default {
return CallDepthThreadLocalMap.incrementCallDepth(CouchbaseCluster.class); return CallDepthThreadLocalMap.incrementCallDepth(CouchbaseCluster.class);
} }
@Advice.OnMethodExit @Advice.OnMethodExit(onThrowable = Throwable.class)
public static void subscribeResult( public static void subscribeResult(
@Advice.Enter final int callDepth, @Advice.Enter final int callDepth,
@Advice.Origin final Method method, @Advice.Origin final Method method,

View File

@ -1,7 +1,6 @@
package datadog.trace.instrumentation.couchbase.client; package datadog.trace.instrumentation.couchbase.client;
import static java.util.Collections.singletonMap; import static java.util.Collections.singletonMap;
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
import static net.bytebuddy.matcher.ElementMatchers.isMethod; import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.isPublic; import static net.bytebuddy.matcher.ElementMatchers.isPublic;
import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.named;
@ -29,10 +28,8 @@ public class CouchbaseClusterInstrumentation extends Instrumenter.Default {
@Override @Override
public ElementMatcher<TypeDescription> typeMatcher() { public ElementMatcher<TypeDescription> typeMatcher() {
return not(isInterface()) return named("com.couchbase.client.java.cluster.DefaultAsyncClusterManager")
.and( .or(named("com.couchbase.client.java.CouchbaseAsyncCluster"));
named("com.couchbase.client.java.cluster.DefaultAsyncClusterManager")
.or(named("com.couchbase.client.java.CouchbaseAsyncCluster")));
} }
@Override @Override
@ -64,7 +61,7 @@ public class CouchbaseClusterInstrumentation extends Instrumenter.Default {
return CallDepthThreadLocalMap.incrementCallDepth(CouchbaseCluster.class); return CallDepthThreadLocalMap.incrementCallDepth(CouchbaseCluster.class);
} }
@Advice.OnMethodExit @Advice.OnMethodExit(onThrowable = Throwable.class)
public static void subscribeResult( public static void subscribeResult(
@Advice.Enter final int callDepth, @Advice.Enter final int callDepth,
@Advice.Origin final Method method, @Advice.Origin final Method method,

View File

@ -1,8 +1,9 @@
package datadog.trace.instrumentation.couchbase.client; package datadog.trace.instrumentation.couchbase.client;
import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.safeHasSuperType; import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.extendsClass;
import static java.util.Collections.singletonMap; import static java.util.Collections.singletonMap;
import static net.bytebuddy.matcher.ElementMatchers.isMethod; import static net.bytebuddy.matcher.ElementMatchers.isMethod;
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.takesArgument; import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import static net.bytebuddy.matcher.ElementMatchers.takesArguments; import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
@ -31,7 +32,9 @@ public class CouchbaseNetworkInstrumentation extends Instrumenter.Default {
@Override @Override
public ElementMatcher<? super TypeDescription> typeMatcher() { public ElementMatcher<? super TypeDescription> typeMatcher() {
// Exact class because private fields are used // Exact class because private fields are used
return safeHasSuperType(named("com.couchbase.client.core.endpoint.AbstractGenericHandler")); return nameStartsWith("com.couchbase.client.")
.<TypeDescription>and(
extendsClass(named("com.couchbase.client.core.endpoint.AbstractGenericHandler")));
} }
@Override @Override

View File

@ -1,15 +1,13 @@
package datadog.trace.instrumentation.dropwizard.view; package datadog.trace.instrumentation.dropwizard.view;
import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.safeHasSuperType; import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.implementsInterface;
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activateSpan; import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activateSpan;
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activeSpan; import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activeSpan;
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan; import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan;
import static java.util.Collections.singletonMap; import static java.util.Collections.singletonMap;
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
import static net.bytebuddy.matcher.ElementMatchers.isMethod; import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.isPublic; import static net.bytebuddy.matcher.ElementMatchers.isPublic;
import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.not;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument; import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import com.google.auto.service.AutoService; import com.google.auto.service.AutoService;
@ -34,7 +32,7 @@ public final class DropwizardViewInstrumentation extends Instrumenter.Default {
@Override @Override
public ElementMatcher<TypeDescription> typeMatcher() { public ElementMatcher<TypeDescription> typeMatcher() {
return not(isInterface()).and(safeHasSuperType(named("io.dropwizard.views.ViewRenderer"))); return implementsInterface(named("io.dropwizard.views.ViewRenderer"));
} }
@Override @Override

View File

@ -113,7 +113,6 @@ class DropwizardTest extends HttpServerTest<DropwizardTestSupport, Servlet3Decor
tags { tags {
"$Tags.COMPONENT" serverDecorator.component() "$Tags.COMPONENT" serverDecorator.component()
"$Tags.SPAN_KIND" Tags.SPAN_KIND_SERVER "$Tags.SPAN_KIND" Tags.SPAN_KIND_SERVER
"$Tags.PEER_HOSTNAME" { it == "localhost" || it == "127.0.0.1" }
"$Tags.PEER_HOST_IPV4" { it == null || it == "127.0.0.1" } // Optional "$Tags.PEER_HOST_IPV4" { it == null || it == "127.0.0.1" } // Optional
"$Tags.PEER_PORT" Integer "$Tags.PEER_PORT" Integer
"$Tags.HTTP_URL" "${endpoint.resolve(address)}" "$Tags.HTTP_URL" "${endpoint.resolve(address)}"

View File

@ -4,10 +4,8 @@ import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activateSp
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan; import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan;
import static datadog.trace.instrumentation.elasticsearch.ElasticsearchRestClientDecorator.DECORATE; import static datadog.trace.instrumentation.elasticsearch.ElasticsearchRestClientDecorator.DECORATE;
import static java.util.Collections.singletonMap; import static java.util.Collections.singletonMap;
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
import static net.bytebuddy.matcher.ElementMatchers.isMethod; import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.not;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument; import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import static net.bytebuddy.matcher.ElementMatchers.takesArguments; import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
@ -42,7 +40,7 @@ public class Elasticsearch5RestClientInstrumentation extends Instrumenter.Defaul
@Override @Override
public ElementMatcher<TypeDescription> typeMatcher() { public ElementMatcher<TypeDescription> typeMatcher() {
return not(isInterface()).and(named("org.elasticsearch.client.RestClient")); return named("org.elasticsearch.client.RestClient");
} }
@Override @Override

View File

@ -4,10 +4,8 @@ import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activateSp
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan; import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan;
import static datadog.trace.instrumentation.elasticsearch.ElasticsearchRestClientDecorator.DECORATE; import static datadog.trace.instrumentation.elasticsearch.ElasticsearchRestClientDecorator.DECORATE;
import static java.util.Collections.singletonMap; import static java.util.Collections.singletonMap;
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
import static net.bytebuddy.matcher.ElementMatchers.isMethod; import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.not;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument; import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import static net.bytebuddy.matcher.ElementMatchers.takesArguments; import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
@ -43,7 +41,7 @@ public class Elasticsearch6RestClientInstrumentation extends Instrumenter.Defaul
@Override @Override
public ElementMatcher<TypeDescription> typeMatcher() { public ElementMatcher<TypeDescription> typeMatcher() {
return not(isInterface()).and(named("org.elasticsearch.client.RestClient")); return named("org.elasticsearch.client.RestClient");
} }
@Override @Override

View File

@ -4,10 +4,8 @@ import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activateSp
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan; import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan;
import static datadog.trace.instrumentation.elasticsearch.ElasticsearchTransportClientDecorator.DECORATE; import static datadog.trace.instrumentation.elasticsearch.ElasticsearchTransportClientDecorator.DECORATE;
import static java.util.Collections.singletonMap; import static java.util.Collections.singletonMap;
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
import static net.bytebuddy.matcher.ElementMatchers.isMethod; import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.not;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument; import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import com.google.auto.service.AutoService; import com.google.auto.service.AutoService;
@ -35,7 +33,7 @@ public class Elasticsearch2TransportClientInstrumentation extends Instrumenter.D
public ElementMatcher<TypeDescription> typeMatcher() { public ElementMatcher<TypeDescription> typeMatcher() {
// If we want to be more generic, we could instrument the interface instead: // If we want to be more generic, we could instrument the interface instead:
// .and(safeHasSuperType(named("org.elasticsearch.client.ElasticsearchClient")))) // .and(safeHasSuperType(named("org.elasticsearch.client.ElasticsearchClient"))))
return not(isInterface()).and(named("org.elasticsearch.client.support.AbstractClient")); return named("org.elasticsearch.client.support.AbstractClient");
} }
@Override @Override

View File

@ -76,7 +76,7 @@ public class TransportActionListener<T extends ActionResponse> implements Action
if (response instanceof BaseNodesResponse) { if (response instanceof BaseNodesResponse) {
final BaseNodesResponse resp = (BaseNodesResponse) response; final BaseNodesResponse resp = (BaseNodesResponse) response;
if (resp.failures().length > 0) { if (resp.failures() != null && resp.failures().length > 0) {
span.setTag("elasticsearch.node.failures", resp.failures().length); span.setTag("elasticsearch.node.failures", resp.failures().length);
} }
span.setTag("elasticsearch.node.cluster.name", resp.getClusterName().value()); span.setTag("elasticsearch.node.cluster.name", resp.getClusterName().value());

View File

@ -4,6 +4,7 @@ import datadog.trace.agent.test.utils.PortUtils
import datadog.trace.api.DDSpanTypes import datadog.trace.api.DDSpanTypes
import datadog.trace.bootstrap.instrumentation.api.Tags import datadog.trace.bootstrap.instrumentation.api.Tags
import org.elasticsearch.action.admin.cluster.health.ClusterHealthRequest import org.elasticsearch.action.admin.cluster.health.ClusterHealthRequest
import org.elasticsearch.action.admin.cluster.stats.ClusterStatsRequest
import org.elasticsearch.client.transport.TransportClient import org.elasticsearch.client.transport.TransportClient
import org.elasticsearch.common.io.FileSystemUtils import org.elasticsearch.common.io.FileSystemUtils
import org.elasticsearch.common.settings.Settings import org.elasticsearch.common.settings.Settings
@ -104,6 +105,41 @@ class Elasticsearch2TransportClientTest extends AgentTestRunner {
} }
} }
def "test elasticsearch stats"() {
setup:
def result = client.admin().cluster().clusterStats(new ClusterStatsRequest(new String[0]))
def status = result.get().status
def failures = result.get().failures()
expect:
status.name() == "GREEN"
failures == null
assertTraces(1) {
trace(0, 1) {
span(0) {
serviceName "elasticsearch"
resourceName "ClusterStatsAction"
operationName "elasticsearch.query"
spanType DDSpanTypes.ELASTICSEARCH
tags {
"$Tags.COMPONENT" "elasticsearch-java"
"$Tags.SPAN_KIND" Tags.SPAN_KIND_CLIENT
"$Tags.PEER_HOSTNAME" "127.0.0.1"
"$Tags.PEER_HOST_IPV4" "127.0.0.1"
"$Tags.PEER_PORT" tcpPort
"$Tags.DB_TYPE" "elasticsearch"
"elasticsearch.action" "ClusterStatsAction"
"elasticsearch.request" "ClusterStatsRequest"
"elasticsearch.node.cluster.name" "test-cluster"
defaultTags()
}
}
}
}
}
def "test elasticsearch error"() { def "test elasticsearch error"() {
when: when:
client.prepareGet(indexName, indexType, id).get() client.prepareGet(indexName, indexType, id).get()

View File

@ -4,10 +4,8 @@ import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activateSp
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan; import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan;
import static datadog.trace.instrumentation.elasticsearch.ElasticsearchTransportClientDecorator.DECORATE; import static datadog.trace.instrumentation.elasticsearch.ElasticsearchTransportClientDecorator.DECORATE;
import static java.util.Collections.singletonMap; import static java.util.Collections.singletonMap;
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
import static net.bytebuddy.matcher.ElementMatchers.isMethod; import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.not;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument; import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import com.google.auto.service.AutoService; import com.google.auto.service.AutoService;
@ -36,7 +34,7 @@ public class Elasticsearch53TransportClientInstrumentation extends Instrumenter.
public ElementMatcher<TypeDescription> typeMatcher() { public ElementMatcher<TypeDescription> typeMatcher() {
// If we want to be more generic, we could instrument the interface instead: // If we want to be more generic, we could instrument the interface instead:
// .and(safeHasSuperType(named("org.elasticsearch.client.ElasticsearchClient")))) // .and(safeHasSuperType(named("org.elasticsearch.client.ElasticsearchClient"))))
return not(isInterface()).and(named("org.elasticsearch.client.support.AbstractClient")); return named("org.elasticsearch.client.support.AbstractClient");
} }
@Override @Override

View File

@ -4,10 +4,8 @@ import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activateSp
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan; import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan;
import static datadog.trace.instrumentation.elasticsearch.ElasticsearchTransportClientDecorator.DECORATE; import static datadog.trace.instrumentation.elasticsearch.ElasticsearchTransportClientDecorator.DECORATE;
import static java.util.Collections.singletonMap; import static java.util.Collections.singletonMap;
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
import static net.bytebuddy.matcher.ElementMatchers.isMethod; import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.not;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument; import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import com.google.auto.service.AutoService; import com.google.auto.service.AutoService;
@ -35,7 +33,7 @@ public class Elasticsearch5TransportClientInstrumentation extends Instrumenter.D
public ElementMatcher<TypeDescription> typeMatcher() { public ElementMatcher<TypeDescription> typeMatcher() {
// If we want to be more generic, we could instrument the interface instead: // If we want to be more generic, we could instrument the interface instead:
// .and(safeHasSuperType(named("org.elasticsearch.client.ElasticsearchClient")))) // .and(safeHasSuperType(named("org.elasticsearch.client.ElasticsearchClient"))))
return not(isInterface()).and(named("org.elasticsearch.client.support.AbstractClient")); return named("org.elasticsearch.client.support.AbstractClient");
} }
@Override @Override

View File

@ -4,10 +4,8 @@ import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activateSp
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan; import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan;
import static datadog.trace.instrumentation.elasticsearch.ElasticsearchTransportClientDecorator.DECORATE; import static datadog.trace.instrumentation.elasticsearch.ElasticsearchTransportClientDecorator.DECORATE;
import static java.util.Collections.singletonMap; import static java.util.Collections.singletonMap;
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
import static net.bytebuddy.matcher.ElementMatchers.isMethod; import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.not;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument; import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import com.google.auto.service.AutoService; import com.google.auto.service.AutoService;
@ -39,7 +37,7 @@ public class Elasticsearch6TransportClientInstrumentation extends Instrumenter.D
public ElementMatcher<TypeDescription> typeMatcher() { public ElementMatcher<TypeDescription> typeMatcher() {
// If we want to be more generic, we could instrument the interface instead: // If we want to be more generic, we could instrument the interface instead:
// .and(safeHasSuperType(named("org.elasticsearch.client.ElasticsearchClient")))) // .and(safeHasSuperType(named("org.elasticsearch.client.ElasticsearchClient"))))
return not(isInterface()).and(named("org.elasticsearch.client.support.AbstractClient")); return named("org.elasticsearch.client.support.AbstractClient");
} }
@Override @Override

View File

@ -24,11 +24,6 @@ public class FinatraDecorator extends HttpServerDecorator<Request, Request, Resp
return URI.create(request.uri()); return URI.create(request.uri());
} }
@Override
protected String peerHostname(final Request request) {
return request.remoteHost();
}
@Override @Override
protected String peerHostIP(final Request request) { protected String peerHostIP(final Request request) {
return request.remoteAddress().getHostAddress(); return request.remoteAddress().getHostAddress();

View File

@ -1,15 +1,14 @@
package datadog.trace.instrumentation.finatra; package datadog.trace.instrumentation.finatra;
import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.safeHasSuperType; import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.extendsClass;
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activateSpan; import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activateSpan;
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activeSpan; import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activeSpan;
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan; import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan;
import static datadog.trace.instrumentation.finatra.FinatraDecorator.DECORATE; import static datadog.trace.instrumentation.finatra.FinatraDecorator.DECORATE;
import static java.util.Collections.singletonMap; import static java.util.Collections.singletonMap;
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
import static net.bytebuddy.matcher.ElementMatchers.isMethod; import static net.bytebuddy.matcher.ElementMatchers.isMethod;
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.not;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument; import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import static net.bytebuddy.matcher.ElementMatchers.takesArguments; import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
@ -51,8 +50,9 @@ public class FinatraInstrumentation extends Instrumenter.Default {
@Override @Override
public ElementMatcher<? super TypeDescription> typeMatcher() { public ElementMatcher<? super TypeDescription> typeMatcher() {
return not(isInterface()) return nameStartsWith("com.twitter.finatra.")
.and(safeHasSuperType(named("com.twitter.finatra.http.internal.routing.Route"))); .<TypeDescription>and(
extendsClass(named("com.twitter.finatra.http.internal.routing.Route")));
} }
@Override @Override

View File

@ -103,7 +103,6 @@ class GlassFishServerTest extends HttpServerTest<GlassFish, Servlet3Decorator> {
tags { tags {
"$Tags.COMPONENT" serverDecorator.component() "$Tags.COMPONENT" serverDecorator.component()
"$Tags.SPAN_KIND" Tags.SPAN_KIND_SERVER "$Tags.SPAN_KIND" Tags.SPAN_KIND_SERVER
"$Tags.PEER_HOSTNAME" { it == "localhost" || it == "127.0.0.1" }
"$Tags.PEER_HOST_IPV4" { it == null || it == "127.0.0.1" } // Optional "$Tags.PEER_HOST_IPV4" { it == null || it == "127.0.0.1" } // Optional
"$Tags.PEER_PORT" Integer "$Tags.PEER_PORT" Integer
"$Tags.HTTP_STATUS" endpoint.status "$Tags.HTTP_STATUS" endpoint.status

View File

@ -26,11 +26,6 @@ public class GrizzlyDecorator extends HttpServerDecorator<Request, Request, Resp
null); null);
} }
@Override
protected String peerHostname(final Request request) {
return request.getRemoteHost();
}
@Override @Override
protected String peerHostIP(final Request request) { protected String peerHostIP(final Request request) {
return request.getRemoteAddr(); return request.getRemoteAddr();

View File

@ -1,11 +1,9 @@
package datadog.trace.instrumentation.hibernate.core.v3_3; package datadog.trace.instrumentation.hibernate.core.v3_3;
import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.safeHasSuperType; import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.implementsInterface;
import static java.util.Collections.singletonMap; import static java.util.Collections.singletonMap;
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
import static net.bytebuddy.matcher.ElementMatchers.isMethod; import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.not;
import com.google.auto.service.AutoService; import com.google.auto.service.AutoService;
import datadog.trace.agent.tooling.Instrumenter; import datadog.trace.agent.tooling.Instrumenter;
@ -31,7 +29,7 @@ public class CriteriaInstrumentation extends AbstractHibernateInstrumentation {
@Override @Override
public ElementMatcher<TypeDescription> typeMatcher() { public ElementMatcher<TypeDescription> typeMatcher() {
return not(isInterface()).and(safeHasSuperType(named("org.hibernate.Criteria"))); return implementsInterface(named("org.hibernate.Criteria"));
} }
@Override @Override

View File

@ -1,12 +1,10 @@
package datadog.trace.instrumentation.hibernate.core.v3_3; package datadog.trace.instrumentation.hibernate.core.v3_3;
import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.safeHasSuperType; import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.implementsInterface;
import static datadog.trace.instrumentation.hibernate.HibernateDecorator.DECORATOR; import static datadog.trace.instrumentation.hibernate.HibernateDecorator.DECORATOR;
import static java.util.Collections.singletonMap; import static java.util.Collections.singletonMap;
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
import static net.bytebuddy.matcher.ElementMatchers.isMethod; import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.not;
import com.google.auto.service.AutoService; import com.google.auto.service.AutoService;
import datadog.trace.agent.tooling.Instrumenter; import datadog.trace.agent.tooling.Instrumenter;
@ -33,7 +31,7 @@ public class QueryInstrumentation extends AbstractHibernateInstrumentation {
@Override @Override
public ElementMatcher<TypeDescription> typeMatcher() { public ElementMatcher<TypeDescription> typeMatcher() {
return not(isInterface()).and(safeHasSuperType(named("org.hibernate.Query"))); return implementsInterface(named("org.hibernate.Query"));
} }
@Override @Override

View File

@ -1,13 +1,12 @@
package datadog.trace.instrumentation.hibernate.core.v3_3; package datadog.trace.instrumentation.hibernate.core.v3_3;
import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.safeHasSuperType; import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.hasInterface;
import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.implementsInterface;
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan; import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan;
import static datadog.trace.instrumentation.hibernate.HibernateDecorator.DECORATOR; import static datadog.trace.instrumentation.hibernate.HibernateDecorator.DECORATOR;
import static java.util.Collections.singletonMap; import static java.util.Collections.singletonMap;
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
import static net.bytebuddy.matcher.ElementMatchers.isMethod; import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.not;
import static net.bytebuddy.matcher.ElementMatchers.returns; import static net.bytebuddy.matcher.ElementMatchers.returns;
import static net.bytebuddy.matcher.ElementMatchers.takesArguments; import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
@ -40,7 +39,7 @@ public class SessionFactoryInstrumentation extends AbstractHibernateInstrumentat
@Override @Override
public ElementMatcher<TypeDescription> typeMatcher() { public ElementMatcher<TypeDescription> typeMatcher() {
return not(isInterface()).and(safeHasSuperType(named("org.hibernate.SessionFactory"))); return implementsInterface(named("org.hibernate.SessionFactory"));
} }
@Override @Override
@ -53,7 +52,7 @@ public class SessionFactoryInstrumentation extends AbstractHibernateInstrumentat
returns( returns(
named("org.hibernate.Session") named("org.hibernate.Session")
.or(named("org.hibernate.StatelessSession")) .or(named("org.hibernate.StatelessSession"))
.or(safeHasSuperType(named("org.hibernate.Session"))))), .or(hasInterface(named("org.hibernate.Session"))))),
SessionFactoryInstrumentation.class.getName() + "$SessionFactoryAdvice"); SessionFactoryInstrumentation.class.getName() + "$SessionFactoryAdvice");
} }

View File

@ -1,12 +1,11 @@
package datadog.trace.instrumentation.hibernate.core.v3_3; package datadog.trace.instrumentation.hibernate.core.v3_3;
import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.safeHasSuperType; import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.hasInterface;
import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.implementsInterface;
import static datadog.trace.instrumentation.hibernate.HibernateDecorator.DECORATOR; import static datadog.trace.instrumentation.hibernate.HibernateDecorator.DECORATOR;
import static datadog.trace.instrumentation.hibernate.SessionMethodUtils.SCOPE_ONLY_METHODS; import static datadog.trace.instrumentation.hibernate.SessionMethodUtils.SCOPE_ONLY_METHODS;
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
import static net.bytebuddy.matcher.ElementMatchers.isMethod; import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.not;
import static net.bytebuddy.matcher.ElementMatchers.returns; import static net.bytebuddy.matcher.ElementMatchers.returns;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument; import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import static net.bytebuddy.matcher.ElementMatchers.takesArguments; import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
@ -48,10 +47,8 @@ public class SessionInstrumentation extends AbstractHibernateInstrumentation {
@Override @Override
public ElementMatcher<TypeDescription> typeMatcher() { public ElementMatcher<TypeDescription> typeMatcher() {
return not(isInterface()) return implementsInterface(
.and( named("org.hibernate.Session").or(named("org.hibernate.StatelessSession")));
safeHasSuperType(
named("org.hibernate.Session").or(named("org.hibernate.StatelessSession"))));
} }
@Override @Override
@ -99,11 +96,11 @@ public class SessionInstrumentation extends AbstractHibernateInstrumentation {
SessionInstrumentation.class.getName() + "$GetTransactionAdvice"); SessionInstrumentation.class.getName() + "$GetTransactionAdvice");
transformers.put( transformers.put(
isMethod().and(returns(safeHasSuperType(named("org.hibernate.Query")))), isMethod().and(returns(hasInterface(named("org.hibernate.Query")))),
SessionInstrumentation.class.getName() + "$GetQueryAdvice"); SessionInstrumentation.class.getName() + "$GetQueryAdvice");
transformers.put( transformers.put(
isMethod().and(returns(safeHasSuperType(named("org.hibernate.Criteria")))), isMethod().and(returns(hasInterface(named("org.hibernate.Criteria")))),
SessionInstrumentation.class.getName() + "$GetCriteriaAdvice"); SessionInstrumentation.class.getName() + "$GetCriteriaAdvice");
return transformers; return transformers;

View File

@ -1,11 +1,9 @@
package datadog.trace.instrumentation.hibernate.core.v3_3; package datadog.trace.instrumentation.hibernate.core.v3_3;
import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.safeHasSuperType; import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.implementsInterface;
import static java.util.Collections.singletonMap; import static java.util.Collections.singletonMap;
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
import static net.bytebuddy.matcher.ElementMatchers.isMethod; import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.not;
import static net.bytebuddy.matcher.ElementMatchers.takesArguments; import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
import com.google.auto.service.AutoService; import com.google.auto.service.AutoService;
@ -31,7 +29,7 @@ public class TransactionInstrumentation extends AbstractHibernateInstrumentation
@Override @Override
public ElementMatcher<TypeDescription> typeMatcher() { public ElementMatcher<TypeDescription> typeMatcher() {
return not(isInterface()).and(safeHasSuperType(named("org.hibernate.Transaction"))); return implementsInterface(named("org.hibernate.Transaction"));
} }
@Override @Override

View File

@ -1,11 +1,9 @@
package datadog.trace.instrumentation.hibernate.core.v4_0; package datadog.trace.instrumentation.hibernate.core.v4_0;
import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.safeHasSuperType; import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.implementsInterface;
import static java.util.Collections.singletonMap; import static java.util.Collections.singletonMap;
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
import static net.bytebuddy.matcher.ElementMatchers.isMethod; import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.not;
import com.google.auto.service.AutoService; import com.google.auto.service.AutoService;
import datadog.trace.agent.tooling.Instrumenter; import datadog.trace.agent.tooling.Instrumenter;
@ -31,7 +29,7 @@ public class CriteriaInstrumentation extends AbstractHibernateInstrumentation {
@Override @Override
public ElementMatcher<TypeDescription> typeMatcher() { public ElementMatcher<TypeDescription> typeMatcher() {
return not(isInterface()).and(safeHasSuperType(named("org.hibernate.Criteria"))); return implementsInterface(named("org.hibernate.Criteria"));
} }
@Override @Override

View File

@ -1,12 +1,10 @@
package datadog.trace.instrumentation.hibernate.core.v4_0; package datadog.trace.instrumentation.hibernate.core.v4_0;
import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.safeHasSuperType; import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.implementsInterface;
import static datadog.trace.instrumentation.hibernate.HibernateDecorator.DECORATOR; import static datadog.trace.instrumentation.hibernate.HibernateDecorator.DECORATOR;
import static java.util.Collections.singletonMap; import static java.util.Collections.singletonMap;
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
import static net.bytebuddy.matcher.ElementMatchers.isMethod; import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.not;
import com.google.auto.service.AutoService; import com.google.auto.service.AutoService;
import datadog.trace.agent.tooling.Instrumenter; import datadog.trace.agent.tooling.Instrumenter;
@ -33,7 +31,7 @@ public class QueryInstrumentation extends AbstractHibernateInstrumentation {
@Override @Override
public ElementMatcher<TypeDescription> typeMatcher() { public ElementMatcher<TypeDescription> typeMatcher() {
return not(isInterface()).and(safeHasSuperType(named("org.hibernate.Query"))); return implementsInterface(named("org.hibernate.Query"));
} }
@Override @Override

View File

@ -1,13 +1,11 @@
package datadog.trace.instrumentation.hibernate.core.v4_0; package datadog.trace.instrumentation.hibernate.core.v4_0;
import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.safeHasSuperType; import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.implementsInterface;
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan; import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan;
import static datadog.trace.instrumentation.hibernate.HibernateDecorator.DECORATOR; import static datadog.trace.instrumentation.hibernate.HibernateDecorator.DECORATOR;
import static java.util.Collections.singletonMap; import static java.util.Collections.singletonMap;
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
import static net.bytebuddy.matcher.ElementMatchers.isMethod; import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.not;
import static net.bytebuddy.matcher.ElementMatchers.returns; import static net.bytebuddy.matcher.ElementMatchers.returns;
import static net.bytebuddy.matcher.ElementMatchers.takesArguments; import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
@ -34,7 +32,7 @@ public class SessionFactoryInstrumentation extends AbstractHibernateInstrumentat
@Override @Override
public ElementMatcher<TypeDescription> typeMatcher() { public ElementMatcher<TypeDescription> typeMatcher() {
return not(isInterface()).and(safeHasSuperType(named("org.hibernate.SessionFactory"))); return implementsInterface(named("org.hibernate.SessionFactory"));
} }
@Override @Override

View File

@ -1,12 +1,11 @@
package datadog.trace.instrumentation.hibernate.core.v4_0; package datadog.trace.instrumentation.hibernate.core.v4_0;
import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.safeHasSuperType; import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.hasInterface;
import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.implementsInterface;
import static datadog.trace.instrumentation.hibernate.HibernateDecorator.DECORATOR; import static datadog.trace.instrumentation.hibernate.HibernateDecorator.DECORATOR;
import static datadog.trace.instrumentation.hibernate.SessionMethodUtils.SCOPE_ONLY_METHODS; import static datadog.trace.instrumentation.hibernate.SessionMethodUtils.SCOPE_ONLY_METHODS;
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
import static net.bytebuddy.matcher.ElementMatchers.isMethod; import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.not;
import static net.bytebuddy.matcher.ElementMatchers.returns; import static net.bytebuddy.matcher.ElementMatchers.returns;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument; import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import static net.bytebuddy.matcher.ElementMatchers.takesArguments; import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
@ -46,7 +45,7 @@ public class SessionInstrumentation extends AbstractHibernateInstrumentation {
@Override @Override
public ElementMatcher<TypeDescription> typeMatcher() { public ElementMatcher<TypeDescription> typeMatcher() {
return not(isInterface()).and(safeHasSuperType(named("org.hibernate.SharedSessionContract"))); return implementsInterface(named("org.hibernate.SharedSessionContract"));
} }
@Override @Override
@ -93,11 +92,11 @@ public class SessionInstrumentation extends AbstractHibernateInstrumentation {
SessionInstrumentation.class.getName() + "$GetTransactionAdvice"); SessionInstrumentation.class.getName() + "$GetTransactionAdvice");
transformers.put( transformers.put(
isMethod().and(returns(safeHasSuperType(named("org.hibernate.Query")))), isMethod().and(returns(hasInterface(named("org.hibernate.Query")))),
SessionInstrumentation.class.getName() + "$GetQueryAdvice"); SessionInstrumentation.class.getName() + "$GetQueryAdvice");
transformers.put( transformers.put(
isMethod().and(returns(safeHasSuperType(named("org.hibernate.Criteria")))), isMethod().and(returns(hasInterface(named("org.hibernate.Criteria")))),
SessionInstrumentation.class.getName() + "$GetCriteriaAdvice"); SessionInstrumentation.class.getName() + "$GetCriteriaAdvice");
return transformers; return transformers;

View File

@ -1,11 +1,9 @@
package datadog.trace.instrumentation.hibernate.core.v4_0; package datadog.trace.instrumentation.hibernate.core.v4_0;
import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.safeHasSuperType; import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.implementsInterface;
import static java.util.Collections.singletonMap; import static java.util.Collections.singletonMap;
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
import static net.bytebuddy.matcher.ElementMatchers.isMethod; import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.not;
import static net.bytebuddy.matcher.ElementMatchers.takesArguments; import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
import com.google.auto.service.AutoService; import com.google.auto.service.AutoService;
@ -31,7 +29,7 @@ public class TransactionInstrumentation extends AbstractHibernateInstrumentation
@Override @Override
public ElementMatcher<TypeDescription> typeMatcher() { public ElementMatcher<TypeDescription> typeMatcher() {
return not(isInterface()).and(safeHasSuperType(named("org.hibernate.Transaction"))); return implementsInterface(named("org.hibernate.Transaction"));
} }
@Override @Override

View File

@ -1,11 +1,9 @@
package datadog.trace.instrumentation.hibernate.core.v4_3; package datadog.trace.instrumentation.hibernate.core.v4_3;
import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.safeHasSuperType; import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.implementsInterface;
import static java.util.Collections.singletonMap; import static java.util.Collections.singletonMap;
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
import static net.bytebuddy.matcher.ElementMatchers.isMethod; import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.not;
import com.google.auto.service.AutoService; import com.google.auto.service.AutoService;
import datadog.trace.agent.tooling.Instrumenter; import datadog.trace.agent.tooling.Instrumenter;
@ -47,7 +45,7 @@ public class ProcedureCallInstrumentation extends Instrumenter.Default {
@Override @Override
public ElementMatcher<TypeDescription> typeMatcher() { public ElementMatcher<TypeDescription> typeMatcher() {
return not(isInterface()).and(safeHasSuperType(named("org.hibernate.procedure.ProcedureCall"))); return implementsInterface(named("org.hibernate.procedure.ProcedureCall"));
} }
@Override @Override

View File

@ -1,10 +1,9 @@
package datadog.trace.instrumentation.hibernate.core.v4_3; package datadog.trace.instrumentation.hibernate.core.v4_3;
import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.safeHasSuperType; import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.hasInterface;
import static net.bytebuddy.matcher.ElementMatchers.isInterface; import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.implementsInterface;
import static net.bytebuddy.matcher.ElementMatchers.isMethod; import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.not;
import static net.bytebuddy.matcher.ElementMatchers.returns; import static net.bytebuddy.matcher.ElementMatchers.returns;
import com.google.auto.service.AutoService; import com.google.auto.service.AutoService;
@ -53,7 +52,7 @@ public class SessionInstrumentation extends Instrumenter.Default {
@Override @Override
public ElementMatcher<TypeDescription> typeMatcher() { public ElementMatcher<TypeDescription> typeMatcher() {
return not(isInterface()).and(safeHasSuperType(named("org.hibernate.SharedSessionContract"))); return implementsInterface(named("org.hibernate.SharedSessionContract"));
} }
@Override @Override
@ -61,7 +60,7 @@ public class SessionInstrumentation extends Instrumenter.Default {
final Map<ElementMatcher<? super MethodDescription>, String> transformers = new HashMap<>(); final Map<ElementMatcher<? super MethodDescription>, String> transformers = new HashMap<>();
transformers.put( transformers.put(
isMethod().and(returns(safeHasSuperType(named("org.hibernate.procedure.ProcedureCall")))), isMethod().and(returns(hasInterface(named("org.hibernate.procedure.ProcedureCall")))),
SessionInstrumentation.class.getName() + "$GetProcedureCallAdvice"); SessionInstrumentation.class.getName() + "$GetProcedureCallAdvice");
return transformers; return transformers;

View File

@ -1,6 +1,6 @@
package datadog.trace.instrumentation.http_url_connection; package datadog.trace.instrumentation.http_url_connection;
import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.safeHasSuperType; import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.extendsClass;
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activateSpan; import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activateSpan;
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.propagate; import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.propagate;
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan; import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan;
@ -9,6 +9,7 @@ import static datadog.trace.instrumentation.http_url_connection.HttpUrlConnectio
import static java.util.Collections.singletonMap; import static java.util.Collections.singletonMap;
import static net.bytebuddy.matcher.ElementMatchers.isMethod; import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.isPublic; import static net.bytebuddy.matcher.ElementMatchers.isPublic;
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.not; import static net.bytebuddy.matcher.ElementMatchers.not;
@ -25,6 +26,7 @@ import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.method.MethodDescription; import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher; import net.bytebuddy.matcher.ElementMatcher;
import net.bytebuddy.matcher.ElementMatchers;
@AutoService(Instrumenter.class) @AutoService(Instrumenter.class)
public class HttpUrlConnectionInstrumentation extends Instrumenter.Default { public class HttpUrlConnectionInstrumentation extends Instrumenter.Default {
@ -35,9 +37,11 @@ public class HttpUrlConnectionInstrumentation extends Instrumenter.Default {
@Override @Override
public ElementMatcher<TypeDescription> typeMatcher() { public ElementMatcher<TypeDescription> typeMatcher() {
return safeHasSuperType(named("java.net.HttpURLConnection")) return nameStartsWith("java.net.")
.or(ElementMatchers.<TypeDescription>nameStartsWith("sun.net"))
// This class is a simple delegator. Skip because it does not update its `connected` field. // This class is a simple delegator. Skip because it does not update its `connected` field.
.and(not(named("sun.net.www.protocol.https.HttpsURLConnectionImpl"))); .and(not(named("sun.net.www.protocol.https.HttpsURLConnectionImpl")))
.and(extendsClass(named("java.net.HttpURLConnection")));
} }
@Override @Override

View File

@ -3,7 +3,10 @@ package datadog.trace.instrumentation.http_url_connection;
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activateSpan; import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activateSpan;
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan; import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan;
import static java.util.Collections.singletonMap; import static java.util.Collections.singletonMap;
import static net.bytebuddy.matcher.ElementMatchers.*; import static net.bytebuddy.matcher.ElementMatchers.is;
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
import static net.bytebuddy.matcher.ElementMatchers.named;
import com.google.auto.service.AutoService; import com.google.auto.service.AutoService;
import datadog.trace.agent.tooling.Instrumenter; import datadog.trace.agent.tooling.Instrumenter;

View File

@ -1,6 +1,6 @@
package datadog.trace.instrumentation.hystrix; package datadog.trace.instrumentation.hystrix;
import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.safeHasSuperType; import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.extendsClass;
import static datadog.trace.instrumentation.hystrix.HystrixDecorator.DECORATE; import static datadog.trace.instrumentation.hystrix.HystrixDecorator.DECORATE;
import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.returns; import static net.bytebuddy.matcher.ElementMatchers.returns;
@ -29,7 +29,7 @@ public class HystrixInstrumentation extends Instrumenter.Default {
@Override @Override
public ElementMatcher<TypeDescription> typeMatcher() { public ElementMatcher<TypeDescription> typeMatcher() {
return safeHasSuperType( return extendsClass(
named("com.netflix.hystrix.HystrixCommand") named("com.netflix.hystrix.HystrixCommand")
.or(named("com.netflix.hystrix.HystrixObservableCommand"))); .or(named("com.netflix.hystrix.HystrixObservableCommand")));
} }

Some files were not shown because too many files have changed in this diff Show More