Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement the TraceInterceptor API #253

Merged
merged 4 commits into from
Mar 12, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ public static ResettableClassFileTransformer installBytebuddyAgent(
}
int numInstrumenters = 0;
for (final Instrumenter instrumenter : ServiceLoader.load(Instrumenter.class)) {
log.debug("Loading instrumentation {}", instrumenter);
log.debug("Loading instrumentation {}", instrumenter.getClass().getName());
agentBuilder = instrumenter.instrument(agentBuilder);
numInstrumenters++;
}
Expand Down
6 changes: 0 additions & 6 deletions dd-java-agent/dd-java-agent.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,6 @@ def includeShadowJar(subproject, jarname) {
relocate 'java.util.logging.Logger', 'datadog.trace.agent.bootstrap.PatchLogger'

if (!project.hasProperty("disableShadowRelocate") || !disableShadowRelocate) {

// These need to be relocated to prevent conflicts in case the regular dd-trace is already on the classpath.
relocate('datadog.trace.api', 'datadog.trace.agent.api') {
// We want to ensure to not miss if a user is using the annotation.
exclude 'datadog.trace.api.Trace'
}
relocate 'datadog.trace.common', 'datadog.trace.agent.common'
relocate 'datadog.opentracing', 'datadog.trace.agent.ot'

Expand Down
12 changes: 12 additions & 0 deletions dd-java-agent/instrumentation/classloaders/classloaders.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
apply from: "${rootDir}/gradle/java.gradle"

dependencies {
compile project(':dd-trace-ot')
compile project(':dd-java-agent:agent-tooling')

compile deps.bytebuddy
compile deps.opentracing
compile deps.autoservice

testCompile project(':dd-java-agent:testing')
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package datadog.trace.instrumentation.classloaders;

import java.util.concurrent.atomic.AtomicInteger;

public class CallDepthThreadLocalMap {
private static final ThreadLocal<AtomicInteger> tls = new ThreadLocal<>();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the count is in a thread-local, why not use an integer instead of an atomic integer?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought about it, but AtomicInteger has nice increment and decrement operations whereas with Integer, I'd have to do those operations then reset on the threadlocal. Still think I should do Integer instead?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, AtomicInteger is fine.


public static int incrementCallDepth() {
AtomicInteger depth = tls.get();
if (depth == null) {
depth = new AtomicInteger(0);
tls.set(depth);
return 0;
} else {
return depth.incrementAndGet();
}
}

public static void reset() {
tls.remove();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package datadog.trace.instrumentation.classloaders;

import static datadog.trace.agent.tooling.ClassLoaderMatcher.classLoaderHasClasses;
import static net.bytebuddy.matcher.ElementMatchers.failSafe;
import static net.bytebuddy.matcher.ElementMatchers.isConstructor;
import static net.bytebuddy.matcher.ElementMatchers.isSubTypeOf;

import com.google.auto.service.AutoService;
import datadog.opentracing.DDTracer;
import datadog.trace.agent.tooling.DDAdvice;
import datadog.trace.agent.tooling.HelperInjector;
import datadog.trace.agent.tooling.Instrumenter;
import io.opentracing.util.GlobalTracer;
import java.lang.reflect.Field;
import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.asm.Advice;

@AutoService(Instrumenter.class)
public final class ClassLoaderInstrumentation extends Instrumenter.Configurable {

public ClassLoaderInstrumentation() {
super("classloader");
}

@Override
protected boolean defaultEnabled() {
return false;
}

@Override
public AgentBuilder apply(final AgentBuilder agentBuilder) {
return agentBuilder
.type(
failSafe(isSubTypeOf(ClassLoader.class)),
classLoaderHasClasses("io.opentracing.util.GlobalTracer"))
.transform(
new HelperInjector(
"datadog.trace.instrumentation.classloaders.CallDepthThreadLocalMap"))
.transform(DDAdvice.create().advice(isConstructor(), ClassloaderAdvice.class.getName()))
.asDecorator();
}

public static class ClassloaderAdvice {

@Advice.OnMethodEnter
public static int constructorEnter() {
// We use this to make sure we only apply the exit instrumentation
// after the constructors are done calling their super constructors.
return CallDepthThreadLocalMap.incrementCallDepth();
}

// Not sure why, but adding suppress causes a verify error.
@Advice.OnMethodExit // (suppress = Throwable.class)
public static void constructorExit(
@Advice.This final ClassLoader cl, @Advice.Enter final int depth) {
if (depth == 0) {
CallDepthThreadLocalMap.reset();

try {
final Field field = GlobalTracer.class.getDeclaredField("tracer");
field.setAccessible(true);

final Object o = field.get(null);
if (o instanceof DDTracer) {
final DDTracer tracer = (DDTracer) o;
tracer.registerClassLoader(cl);
}
} catch (final Throwable e) {
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import datadog.opentracing.DDTracer
import datadog.trace.agent.test.AgentTestRunner
import datadog.trace.agent.test.TestUtils

import java.security.SecureClassLoader

class ClassLoaderInstrumentationTest extends AgentTestRunner {
static {
System.setProperty("dd.integration.classloader.enabled", "true")
}

DDTracer tracer = Mock()

def setup() {
TestUtils.registerOrReplaceGlobalTracer(tracer)
}

def "creating classloader calls register on tracer"() {
when:
new EmptyNonDelegatingLoader()

then:
1 * tracer.registerClassLoader(_ as EmptyNonDelegatingLoader)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does this syntax mean? Not sure what's being asserted.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This means that tracer.registerClassLoader was called one time with an argument of type EmptyNonDelegatingLoader.
Basically a way of asserting the mock interactions.

0 * _
}

def "creating anonymous classloader calls register on tracer"() {
when:
new EmptyNonDelegatingLoader() {}

then:
1 * tracer.registerClassLoader(_ as EmptyNonDelegatingLoader)
0 * _
}

def "bootstrap classloaders aren't instrumented"() {
// (Because they don't have access to GlobalTracer)
when:
new SecureClassLoader()
new SecureClassLoader(null) {}
new URLClassLoader(new URL[0], ClassLoader.systemClassLoader)

then:
0 * tracer.registerClassLoader(_)
0 * _
}

class EmptyNonDelegatingLoader extends SecureClassLoader {
EmptyNonDelegatingLoader() {
super(null)
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package datadog.trace.instrumentation.jaxrs;

import static net.bytebuddy.matcher.ElementMatchers.declaresMethod;
import static net.bytebuddy.matcher.ElementMatchers.failSafe;
import static net.bytebuddy.matcher.ElementMatchers.hasSuperType;
import static net.bytebuddy.matcher.ElementMatchers.isAnnotatedWith;
import static net.bytebuddy.matcher.ElementMatchers.named;
Expand Down Expand Up @@ -38,7 +39,10 @@ protected AgentBuilder apply(final AgentBuilder agentBuilder) {
.type(
hasSuperType(
isAnnotatedWith(named("javax.ws.rs.Path"))
.or(hasSuperType(declaresMethod(isAnnotatedWith(named("javax.ws.rs.Path")))))))
.or(
failSafe(
hasSuperType(
declaresMethod(isAnnotatedWith(named("javax.ws.rs.Path"))))))))
.transform(
DDAdvice.create()
.advice(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package datadog.trace.instrumentation.jdbc;

import static net.bytebuddy.matcher.ElementMatchers.hasSuperType;
import static net.bytebuddy.matcher.ElementMatchers.failSafe;
import static net.bytebuddy.matcher.ElementMatchers.isConstructor;
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
import static net.bytebuddy.matcher.ElementMatchers.isSubTypeOf;
import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith;
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.takesArgument;
Expand All @@ -28,7 +28,7 @@ public ConnectionInstrumentation() {
@Override
public AgentBuilder apply(final AgentBuilder agentBuilder) {
return agentBuilder
.type(not(isInterface()).and(hasSuperType(named(Connection.class.getName()))))
.type(not(isInterface()).and(failSafe(isSubTypeOf(Connection.class))))
.transform(
DDAdvice.create()
.advice(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package datadog.trace.instrumentation.jdbc;

import static io.opentracing.log.Fields.ERROR_OBJECT;
import static net.bytebuddy.matcher.ElementMatchers.hasSuperType;
import static net.bytebuddy.matcher.ElementMatchers.failSafe;
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
import static net.bytebuddy.matcher.ElementMatchers.isSubTypeOf;
import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.not;
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;

Expand Down Expand Up @@ -35,7 +35,7 @@ public PreparedStatementInstrumentation() {
@Override
public AgentBuilder apply(final AgentBuilder agentBuilder) {
return agentBuilder
.type(not(isInterface()).and(hasSuperType(named(PreparedStatement.class.getName()))))
.type(not(isInterface()).and(failSafe(isSubTypeOf(PreparedStatement.class))))
.transform(
DDAdvice.create()
.advice(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package datadog.trace.instrumentation.jdbc;

import static io.opentracing.log.Fields.ERROR_OBJECT;
import static net.bytebuddy.matcher.ElementMatchers.hasSuperType;
import static net.bytebuddy.matcher.ElementMatchers.failSafe;
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
import static net.bytebuddy.matcher.ElementMatchers.isSubTypeOf;
import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.not;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;

Expand Down Expand Up @@ -35,7 +35,7 @@ public StatementInstrumentation() {
@Override
public AgentBuilder apply(final AgentBuilder agentBuilder) {
return agentBuilder
.type(not(isInterface()).and(hasSuperType(named(Statement.class.getName()))))
.type(not(isInterface()).and(failSafe(isSubTypeOf(Statement.class))))
.transform(
DDAdvice.create()
.advice(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import static datadog.trace.agent.tooling.ClassLoaderMatcher.classLoaderHasClasses;
import static datadog.trace.instrumentation.jms.util.JmsUtil.toResourceName;
import static io.opentracing.log.Fields.ERROR_OBJECT;
import static net.bytebuddy.matcher.ElementMatchers.failSafe;
import static net.bytebuddy.matcher.ElementMatchers.hasSuperType;
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
Expand Down Expand Up @@ -45,7 +46,7 @@ public JMS1MessageConsumerInstrumentation() {
public AgentBuilder apply(final AgentBuilder agentBuilder) {
return agentBuilder
.type(
not(isInterface()).and(hasSuperType(named("javax.jms.MessageConsumer"))),
not(isInterface()).and(failSafe(hasSuperType(named("javax.jms.MessageConsumer")))),
not(classLoaderHasClasses("javax.jms.JMSContext", "javax.jms.CompletionListener")))
.transform(JMS1_HELPER_INJECTOR)
.transform(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import static datadog.trace.agent.tooling.ClassLoaderMatcher.classLoaderHasClasses;
import static datadog.trace.instrumentation.jms.util.JmsUtil.toResourceName;
import static io.opentracing.log.Fields.ERROR_OBJECT;
import static net.bytebuddy.matcher.ElementMatchers.failSafe;
import static net.bytebuddy.matcher.ElementMatchers.hasSuperType;
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
Expand Down Expand Up @@ -39,7 +40,7 @@ public JMS1MessageListenerInstrumentation() {
public AgentBuilder apply(final AgentBuilder agentBuilder) {
return agentBuilder
.type(
not(isInterface()).and(hasSuperType(named("javax.jms.MessageListener"))),
not(isInterface()).and(failSafe(hasSuperType(named("javax.jms.MessageListener")))),
not(classLoaderHasClasses("javax.jms.JMSContext", "javax.jms.CompletionListener")))
.transform(JMS1MessageConsumerInstrumentation.JMS1_HELPER_INJECTOR)
.transform(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import static datadog.trace.agent.tooling.ClassLoaderMatcher.classLoaderHasClasses;
import static datadog.trace.instrumentation.jms.util.JmsUtil.toResourceName;
import static io.opentracing.log.Fields.ERROR_OBJECT;
import static net.bytebuddy.matcher.ElementMatchers.failSafe;
import static net.bytebuddy.matcher.ElementMatchers.hasSuperType;
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
Expand Down Expand Up @@ -40,7 +41,7 @@ public JMS1MessageProducerInstrumentation() {
public AgentBuilder apply(final AgentBuilder agentBuilder) {
return agentBuilder
.type(
not(isInterface()).and(hasSuperType(named("javax.jms.MessageProducer"))),
not(isInterface()).and(failSafe(hasSuperType(named("javax.jms.MessageProducer")))),
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These were causing warnings when dealing with injected classes: raphw/byte-buddy#373
Added the failSafe to suppress the noise.

not(classLoaderHasClasses("javax.jms.JMSContext", "javax.jms.CompletionListener")))
.transform(JMS1MessageConsumerInstrumentation.JMS1_HELPER_INJECTOR)
.transform(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import static datadog.trace.agent.tooling.ClassLoaderMatcher.classLoaderHasClasses;
import static datadog.trace.instrumentation.jms.util.JmsUtil.toResourceName;
import static io.opentracing.log.Fields.ERROR_OBJECT;
import static net.bytebuddy.matcher.ElementMatchers.failSafe;
import static net.bytebuddy.matcher.ElementMatchers.hasSuperType;
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
Expand Down Expand Up @@ -45,7 +46,7 @@ public JMS2MessageConsumerInstrumentation() {
public AgentBuilder apply(final AgentBuilder agentBuilder) {
return agentBuilder
.type(
not(isInterface()).and(hasSuperType(named("javax.jms.MessageConsumer"))),
not(isInterface()).and(failSafe(hasSuperType(named("javax.jms.MessageConsumer")))),
classLoaderHasClasses("javax.jms.JMSContext", "javax.jms.CompletionListener"))
.transform(JMS2_HELPER_INJECTOR)
.transform(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import static datadog.trace.agent.tooling.ClassLoaderMatcher.classLoaderHasClasses;
import static datadog.trace.instrumentation.jms.util.JmsUtil.toResourceName;
import static io.opentracing.log.Fields.ERROR_OBJECT;
import static net.bytebuddy.matcher.ElementMatchers.failSafe;
import static net.bytebuddy.matcher.ElementMatchers.hasSuperType;
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
Expand Down Expand Up @@ -39,7 +40,7 @@ public JMS2MessageListenerInstrumentation() {
public AgentBuilder apply(final AgentBuilder agentBuilder) {
return agentBuilder
.type(
not(isInterface()).and(hasSuperType(named("javax.jms.MessageListener"))),
not(isInterface()).and(failSafe(hasSuperType(named("javax.jms.MessageListener")))),
classLoaderHasClasses("javax.jms.JMSContext", "javax.jms.CompletionListener"))
.transform(JMS2MessageConsumerInstrumentation.JMS2_HELPER_INJECTOR)
.transform(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import static datadog.trace.agent.tooling.ClassLoaderMatcher.classLoaderHasClasses;
import static datadog.trace.instrumentation.jms.util.JmsUtil.toResourceName;
import static io.opentracing.log.Fields.ERROR_OBJECT;
import static net.bytebuddy.matcher.ElementMatchers.failSafe;
import static net.bytebuddy.matcher.ElementMatchers.hasSuperType;
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
Expand Down Expand Up @@ -40,7 +41,7 @@ public JMS2MessageProducerInstrumentation() {
public AgentBuilder apply(final AgentBuilder agentBuilder) {
return agentBuilder
.type(
not(isInterface()).and(hasSuperType(named("javax.jms.MessageProducer"))),
not(isInterface()).and(failSafe(hasSuperType(named("javax.jms.MessageProducer")))),
classLoaderHasClasses("javax.jms.JMSContext", "javax.jms.CompletionListener"))
.transform(JMS2MessageConsumerInstrumentation.JMS2_HELPER_INJECTOR)
.transform(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import static datadog.trace.agent.tooling.ClassLoaderMatcher.classLoaderHasClasses;
import static io.opentracing.log.Fields.ERROR_OBJECT;
import static net.bytebuddy.matcher.ElementMatchers.failSafe;
import static net.bytebuddy.matcher.ElementMatchers.hasSuperType;
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
Expand Down Expand Up @@ -42,7 +43,7 @@ public FilterChain2Instrumentation() {
public AgentBuilder apply(final AgentBuilder agentBuilder) {
return agentBuilder
.type(
not(isInterface()).and(hasSuperType(named("javax.servlet.FilterChain"))),
not(isInterface()).and(failSafe(hasSuperType(named("javax.servlet.FilterChain")))),
not(classLoaderHasClasses("javax.servlet.AsyncEvent", "javax.servlet.AsyncListener"))
.and(
classLoaderHasClasses(
Expand Down