Split Runnable and Callable instrumentations

Otherwise `Runnable` may define `call` method leading to
instrumentation exception.
See `org.h2.util.Task` as an example.
This commit is contained in:
Nikolay Martynov 2020-03-05 09:21:02 -05:00
parent 18f9c653b9
commit 4aac30826d
2 changed files with 78 additions and 23 deletions

View File

@ -24,15 +24,15 @@ import net.bytebuddy.matcher.ElementMatcher;
/** Instrument {@link Runnable} and {@Callable} */ /** Instrument {@link Runnable} and {@Callable} */
@Slf4j @Slf4j
@AutoService(Instrumenter.class) @AutoService(Instrumenter.class)
public final class RunnableCallableInstrumentation extends Instrumenter.Default { public final class CallableInstrumentation extends Instrumenter.Default {
public RunnableCallableInstrumentation() { public CallableInstrumentation() {
super(AbstractExecutorInstrumentation.EXEC_NAME); super(AbstractExecutorInstrumentation.EXEC_NAME);
} }
@Override @Override
public ElementMatcher<TypeDescription> typeMatcher() { public ElementMatcher<TypeDescription> typeMatcher() {
return implementsInterface(named(Runnable.class.getName()).or(named(Callable.class.getName()))); return implementsInterface(named(Callable.class.getName()));
} }
@Override @Override
@ -45,7 +45,6 @@ public final class RunnableCallableInstrumentation extends Instrumenter.Default
@Override @Override
public Map<String, String> contextStore() { public Map<String, String> contextStore() {
final Map<String, String> map = new HashMap<>(); final Map<String, String> map = new HashMap<>();
map.put(Runnable.class.getName(), State.class.getName());
map.put(Callable.class.getName(), State.class.getName()); map.put(Callable.class.getName(), State.class.getName());
return Collections.unmodifiableMap(map); return Collections.unmodifiableMap(map);
} }
@ -53,30 +52,12 @@ public final class RunnableCallableInstrumentation extends Instrumenter.Default
@Override @Override
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() { public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
final Map<ElementMatcher<? super MethodDescription>, String> transformers = new HashMap<>(); final Map<ElementMatcher<? super MethodDescription>, String> transformers = new HashMap<>();
transformers.put(
named("run").and(takesArguments(0)).and(isPublic()),
RunnableCallableInstrumentation.class.getName() + "$RunnableAdvice");
transformers.put( transformers.put(
named("call").and(takesArguments(0)).and(isPublic()), named("call").and(takesArguments(0)).and(isPublic()),
RunnableCallableInstrumentation.class.getName() + "$CallableAdvice"); CallableInstrumentation.class.getName() + "$CallableAdvice");
return transformers; return transformers;
} }
public static class RunnableAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static TraceScope enter(@Advice.This final Runnable thiz) {
final ContextStore<Runnable, State> contextStore =
InstrumentationContext.get(Runnable.class, State.class);
return AdviceUtils.startTaskScope(contextStore, thiz);
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void exit(@Advice.Enter final TraceScope scope) {
AdviceUtils.endTaskScope(scope);
}
}
public static class CallableAdvice { public static class CallableAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class) @Advice.OnMethodEnter(suppress = Throwable.class)

View File

@ -0,0 +1,74 @@
package datadog.trace.instrumentation.java.concurrent;
import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.implementsInterface;
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
import com.google.auto.service.AutoService;
import datadog.trace.agent.tooling.Instrumenter;
import datadog.trace.bootstrap.ContextStore;
import datadog.trace.bootstrap.InstrumentationContext;
import datadog.trace.bootstrap.instrumentation.java.concurrent.State;
import datadog.trace.context.TraceScope;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import lombok.extern.slf4j.Slf4j;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
/** Instrument {@link Runnable} and {@Callable} */
@Slf4j
@AutoService(Instrumenter.class)
public final class RunnableInstrumentation extends Instrumenter.Default {
public RunnableInstrumentation() {
super(AbstractExecutorInstrumentation.EXEC_NAME);
}
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return implementsInterface(named(Runnable.class.getName()));
}
@Override
public String[] helperClassNames() {
return new String[] {
AdviceUtils.class.getName(),
};
}
@Override
public Map<String, String> contextStore() {
final Map<String, String> map = new HashMap<>();
map.put(Runnable.class.getName(), State.class.getName());
return Collections.unmodifiableMap(map);
}
@Override
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
final Map<ElementMatcher<? super MethodDescription>, String> transformers = new HashMap<>();
transformers.put(
named("run").and(takesArguments(0)).and(isPublic()),
RunnableInstrumentation.class.getName() + "$RunnableAdvice");
return transformers;
}
public static class RunnableAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static TraceScope enter(@Advice.This final Runnable thiz) {
final ContextStore<Runnable, State> contextStore =
InstrumentationContext.get(Runnable.class, State.class);
return AdviceUtils.startTaskScope(contextStore, thiz);
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void exit(@Advice.Enter final TraceScope scope) {
AdviceUtils.endTaskScope(scope);
}
}
}