diff --git a/instrumentation/executors/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/javaconcurrent/JavaExecutorInstrumentation.java b/instrumentation/executors/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/javaconcurrent/JavaExecutorInstrumentation.java index 809a847f01..4e11267e4e 100644 --- a/instrumentation/executors/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/javaconcurrent/JavaExecutorInstrumentation.java +++ b/instrumentation/executors/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/javaconcurrent/JavaExecutorInstrumentation.java @@ -15,11 +15,8 @@ import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; import io.opentelemetry.javaagent.instrumentation.api.ContextStore; import io.opentelemetry.javaagent.instrumentation.api.InstrumentationContext; import io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge; -import io.opentelemetry.javaagent.instrumentation.api.concurrent.CallableWrapper; import io.opentelemetry.javaagent.instrumentation.api.concurrent.ExecutorAdviceHelper; import io.opentelemetry.javaagent.instrumentation.api.concurrent.PropagatedContext; -import io.opentelemetry.javaagent.instrumentation.api.concurrent.RunnableWrapper; -import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.concurrent.Callable; @@ -74,7 +71,6 @@ public class JavaExecutorInstrumentation extends AbstractExecutorInstrumentation @Advice.Argument(value = 0, readOnly = false) Runnable task) { Context context = Java8BytecodeBridge.currentContext(); if (ExecutorAdviceHelper.shouldPropagateContext(context, task)) { - task = RunnableWrapper.wrapIfNeeded(task); ContextStore contextStore = InstrumentationContext.get(Runnable.class, PropagatedContext.class); return ExecutorAdviceHelper.attachContextToTask(context, contextStore, task); @@ -119,7 +115,6 @@ public class JavaExecutorInstrumentation extends AbstractExecutorInstrumentation @Advice.Argument(value = 0, readOnly = false) Runnable task) { Context context = Java8BytecodeBridge.currentContext(); if (ExecutorAdviceHelper.shouldPropagateContext(context, task)) { - task = RunnableWrapper.wrapIfNeeded(task); ContextStore contextStore = InstrumentationContext.get(Runnable.class, PropagatedContext.class); return ExecutorAdviceHelper.attachContextToTask(context, contextStore, task); @@ -149,7 +144,6 @@ public class JavaExecutorInstrumentation extends AbstractExecutorInstrumentation @Advice.Argument(value = 0, readOnly = false) Callable task) { Context context = Java8BytecodeBridge.currentContext(); if (ExecutorAdviceHelper.shouldPropagateContext(context, task)) { - task = CallableWrapper.wrapIfNeeded(task); ContextStore, PropagatedContext> contextStore = InstrumentationContext.get(Callable.class, PropagatedContext.class); return ExecutorAdviceHelper.attachContextToTask(context, contextStore, task); @@ -181,21 +175,15 @@ public class JavaExecutorInstrumentation extends AbstractExecutorInstrumentation return Collections.emptyList(); } - Collection> wrappedTasks = new ArrayList<>(tasks.size()); Context context = Java8BytecodeBridge.currentContext(); for (Callable task : tasks) { if (ExecutorAdviceHelper.shouldPropagateContext(context, task)) { - Callable newTask = CallableWrapper.wrapIfNeeded(task); - wrappedTasks.add(newTask); ContextStore, PropagatedContext> contextStore = InstrumentationContext.get(Callable.class, PropagatedContext.class); - ExecutorAdviceHelper.attachContextToTask(context, contextStore, newTask); - } else { - // note that task may be null here - wrappedTasks.add(task); + ExecutorAdviceHelper.attachContextToTask(context, contextStore, task); } } - tasks = wrappedTasks; + // returning tasks and not propagatedContexts to avoid allocating another list just for an // edge case (exception) return tasks; @@ -203,8 +191,7 @@ public class JavaExecutorInstrumentation extends AbstractExecutorInstrumentation @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) public static void submitExit( - @Advice.Enter Collection> wrappedTasks, - @Advice.Thrown Throwable throwable) { + @Advice.Enter Collection> tasks, @Advice.Thrown Throwable throwable) { /* Note1: invokeAny doesn't return any futures so all we need to do for it is to make sure we close all scopes in case of an exception. @@ -215,7 +202,7 @@ public class JavaExecutorInstrumentation extends AbstractExecutorInstrumentation (according to ExecutorService docs and AbstractExecutorService code) */ if (throwable != null) { - for (Callable task : wrappedTasks) { + for (Callable task : tasks) { if (task != null) { ContextStore, PropagatedContext> contextStore = InstrumentationContext.get(Callable.class, PropagatedContext.class); diff --git a/instrumentation/guava-10.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/guava/GuavaListenableFutureInstrumentation.java b/instrumentation/guava-10.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/guava/GuavaListenableFutureInstrumentation.java index 231c30fe5b..85429d11b8 100644 --- a/instrumentation/guava-10.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/guava/GuavaListenableFutureInstrumentation.java +++ b/instrumentation/guava-10.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/guava/GuavaListenableFutureInstrumentation.java @@ -16,7 +16,6 @@ import io.opentelemetry.javaagent.instrumentation.api.InstrumentationContext; import io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge; import io.opentelemetry.javaagent.instrumentation.api.concurrent.ExecutorAdviceHelper; import io.opentelemetry.javaagent.instrumentation.api.concurrent.PropagatedContext; -import io.opentelemetry.javaagent.instrumentation.api.concurrent.RunnableWrapper; import java.util.concurrent.Executor; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.type.TypeDescription; @@ -55,7 +54,6 @@ public class GuavaListenableFutureInstrumentation implements TypeInstrumentation @Advice.Argument(value = 0, readOnly = false) Runnable task) { Context context = Java8BytecodeBridge.currentContext(); if (ExecutorAdviceHelper.shouldPropagateContext(context, task)) { - task = RunnableWrapper.wrapIfNeeded(task); ContextStore contextStore = InstrumentationContext.get(Runnable.class, PropagatedContext.class); return ExecutorAdviceHelper.attachContextToTask(context, contextStore, task); diff --git a/instrumentation/internal/internal-lambda/javaagent/build.gradle.kts b/instrumentation/internal/internal-lambda/javaagent/build.gradle.kts new file mode 100644 index 0000000000..70f921cc78 --- /dev/null +++ b/instrumentation/internal/internal-lambda/javaagent/build.gradle.kts @@ -0,0 +1,9 @@ +plugins { + id("otel.javaagent-instrumentation") +} + +dependencies { + compileOnly(project(":javaagent-bootstrap")) + + testImplementation(project(":javaagent-bootstrap")) +} diff --git a/instrumentation/internal/internal-lambda/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/lambda/InnerClassLambdaMetafactoryInstrumentation.java b/instrumentation/internal/internal-lambda/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/lambda/InnerClassLambdaMetafactoryInstrumentation.java new file mode 100644 index 0000000000..234d4fddca --- /dev/null +++ b/instrumentation/internal/internal-lambda/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/lambda/InnerClassLambdaMetafactoryInstrumentation.java @@ -0,0 +1,118 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.internal.lambda; + +import static net.bytebuddy.matcher.ElementMatchers.named; + +import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; +import net.bytebuddy.asm.AsmVisitorWrapper; +import net.bytebuddy.description.field.FieldDescription; +import net.bytebuddy.description.field.FieldList; +import net.bytebuddy.description.method.MethodList; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.implementation.Implementation; +import net.bytebuddy.jar.asm.ClassVisitor; +import net.bytebuddy.jar.asm.ClassWriter; +import net.bytebuddy.jar.asm.MethodVisitor; +import net.bytebuddy.jar.asm.Opcodes; +import net.bytebuddy.jar.asm.Type; +import net.bytebuddy.matcher.ElementMatcher; +import net.bytebuddy.pool.TypePool; + +public class InnerClassLambdaMetafactoryInstrumentation implements TypeInstrumentation { + + @Override + public ElementMatcher typeMatcher() { + return named("java.lang.invoke.InnerClassLambdaMetafactory"); + } + + @Override + public void transform(TypeTransformer transformer) { + transformer.applyTransformer( + (builder, typeDescription, classLoader, module) -> + builder.visit( + new AsmVisitorWrapper() { + @Override + public int mergeWriter(int flags) { + return flags | ClassWriter.COMPUTE_MAXS; + } + + @Override + public int mergeReader(int flags) { + return flags; + } + + @Override + public ClassVisitor wrap( + TypeDescription instrumentedType, + ClassVisitor classVisitor, + Implementation.Context implementationContext, + TypePool typePool, + FieldList fields, + MethodList methods, + int writerFlags, + int readerFlags) { + return new MetaFactoryClassVisitor( + classVisitor, instrumentedType.getInternalName()); + } + })); + } + + private static class MetaFactoryClassVisitor extends ClassVisitor { + private final String slashClassName; + + MetaFactoryClassVisitor(ClassVisitor cv, String slashClassName) { + super(Opcodes.ASM7, cv); + this.slashClassName = slashClassName; + } + + @Override + public MethodVisitor visitMethod( + int access, String name, String descriptor, String signature, String[] exceptions) { + MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions); + // The version of InnerClassLambdaMetafactory used in first version of jdk8 can be seen at + // https://hg.openjdk.java.net/jdk8/jdk8/jdk/file/687fd7c7986d/src/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java + // Depending on jdk version we instrument either spinInnerClass or generateInnerClass. + // We look for a call to ASM ClassWriter.toByteArray() and insert our lambda class + // transformation after it so that defining lambda class will proceed with replaced bytecode. + // This transformation uses ASM instead of Byte-Buddy advice because advice allows adding + // code to the start and end of the method, but here we are modifying a call in the middle of + // the method. + if (("spinInnerClass".equals(name) || "generateInnerClass".equals(name)) + && "()Ljava/lang/Class;".equals(descriptor)) { + mv = + new MethodVisitor(api, mv) { + @Override + public void visitMethodInsn( + int opcode, String owner, String name, String descriptor, boolean isInterface) { + super.visitMethodInsn(opcode, owner, name, descriptor, isInterface); + // if current instruction is a call to ASM ClassWriter.toByteArray() insert call to + // our lambda transformer + if (opcode == Opcodes.INVOKEVIRTUAL + && "toByteArray".equals(name) + && "()[B".equals(descriptor)) { + mv.visitVarInsn(Opcodes.ALOAD, 0); + mv.visitFieldInsn( + Opcodes.GETFIELD, slashClassName, "lambdaClassName", "Ljava/lang/String;"); + mv.visitVarInsn(Opcodes.ALOAD, 0); + // targetClass is used to get the ClassLoader where lambda class will be defined + mv.visitFieldInsn( + Opcodes.GETFIELD, slashClassName, "targetClass", "Ljava/lang/Class;"); + mv.visitMethodInsn( + Opcodes.INVOKESTATIC, + Type.getInternalName(LambdaTransformer.class), + "transform", + "([BLjava/lang/String;Ljava/lang/Class;)[B", + false); + } + } + }; + } + return mv; + } + } +} diff --git a/instrumentation/internal/internal-lambda/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/lambda/LambdaInstrumentationModule.java b/instrumentation/internal/internal-lambda/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/lambda/LambdaInstrumentationModule.java new file mode 100644 index 0000000000..0d9bb8998b --- /dev/null +++ b/instrumentation/internal/internal-lambda/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/lambda/LambdaInstrumentationModule.java @@ -0,0 +1,39 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.internal.lambda; + +import static java.util.Collections.singletonList; + +import com.google.auto.service.AutoService; +import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; +import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import java.util.List; + +@AutoService(InstrumentationModule.class) +public class LambdaInstrumentationModule extends InstrumentationModule { + public LambdaInstrumentationModule() { + super("internal-lambda"); + } + + @Override + public boolean defaultEnabled() { + // internal instrumentations are always enabled by default + return true; + } + + @Override + public List getMuzzleHelperClassNames() { + // this instrumentation uses ASM not ByteBuddy so muzzle doesn't automatically add helper + // classes + return singletonList( + "io.opentelemetry.javaagent.instrumentation.internal.lambda.LambdaTransformer"); + } + + @Override + public List typeInstrumentations() { + return singletonList(new InnerClassLambdaMetafactoryInstrumentation()); + } +} diff --git a/instrumentation/internal/internal-lambda/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/lambda/LambdaTransformer.java b/instrumentation/internal/internal-lambda/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/lambda/LambdaTransformer.java new file mode 100644 index 0000000000..510a020ff2 --- /dev/null +++ b/instrumentation/internal/internal-lambda/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/lambda/LambdaTransformer.java @@ -0,0 +1,38 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.internal.lambda; + +import io.opentelemetry.javaagent.bootstrap.ClassFileTransformerHolder; +import java.lang.instrument.ClassFileTransformer; + +/** Helper class for transforming lambda class bytes. */ +public final class LambdaTransformer { + + private LambdaTransformer() {} + + /** + * Called from {@code java.lang.invoke.InnerClassLambdaMetafactory} to transform lambda class + * bytes. + */ + public static byte[] transform(byte[] classBytes, String slashClassName, Class targetClass) { + ClassFileTransformer transformer = ClassFileTransformerHolder.getClassFileTransformer(); + if (transformer != null) { + try { + byte[] result = + transformer.transform( + targetClass.getClassLoader(), slashClassName, null, null, classBytes); + if (result != null) { + return result; + } + } catch (Throwable throwable) { + // sun.instrument.TransformerManager catches Throwable from ClassFileTransformer and ignores + // it, we do the same. + } + } + + return classBytes; + } +} diff --git a/instrumentation/internal/internal-lambda/javaagent/src/test/groovy/LambdaInstrumentationTest.groovy b/instrumentation/internal/internal-lambda/javaagent/src/test/groovy/LambdaInstrumentationTest.groovy new file mode 100644 index 0000000000..9c406b458a --- /dev/null +++ b/instrumentation/internal/internal-lambda/javaagent/src/test/groovy/LambdaInstrumentationTest.groovy @@ -0,0 +1,21 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification +import io.opentelemetry.javaagent.bootstrap.FieldBackedContextStoreAppliedMarker + +class LambdaInstrumentationTest extends AgentInstrumentationSpecification { + + def "test transform Runnable lambda"() { + setup: + Runnable runnable = TestLambda.makeRunnable() + + expect: + // RunnableInstrumentation adds a ContextStore to all implementors of Runnable. If lambda class + // is transformed then it must have context store marker interface. + runnable instanceof FieldBackedContextStoreAppliedMarker + !FieldBackedContextStoreAppliedMarker.isAssignableFrom(Runnable) + } +} diff --git a/instrumentation/internal/internal-lambda/javaagent/src/test/java/TestLambda.java b/instrumentation/internal/internal-lambda/javaagent/src/test/java/TestLambda.java new file mode 100644 index 0000000000..1039e12a1e --- /dev/null +++ b/instrumentation/internal/internal-lambda/javaagent/src/test/java/TestLambda.java @@ -0,0 +1,10 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +public class TestLambda { + static Runnable makeRunnable() { + return () -> {}; + } +} diff --git a/instrumentation/jetty/jetty-8.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jetty/v8_0/JettyQueuedThreadPoolInstrumentation.java b/instrumentation/jetty/jetty-8.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jetty/v8_0/JettyQueuedThreadPoolInstrumentation.java index 4e25771556..32dd9e04be 100644 --- a/instrumentation/jetty/jetty-8.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jetty/v8_0/JettyQueuedThreadPoolInstrumentation.java +++ b/instrumentation/jetty/jetty-8.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jetty/v8_0/JettyQueuedThreadPoolInstrumentation.java @@ -17,7 +17,6 @@ import io.opentelemetry.javaagent.instrumentation.api.InstrumentationContext; import io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge; import io.opentelemetry.javaagent.instrumentation.api.concurrent.ExecutorAdviceHelper; import io.opentelemetry.javaagent.instrumentation.api.concurrent.PropagatedContext; -import io.opentelemetry.javaagent.instrumentation.api.concurrent.RunnableWrapper; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.matcher.ElementMatcher; @@ -44,7 +43,6 @@ public class JettyQueuedThreadPoolInstrumentation implements TypeInstrumentation @Advice.Argument(value = 0, readOnly = false) Runnable task) { Context context = Java8BytecodeBridge.currentContext(); if (ExecutorAdviceHelper.shouldPropagateContext(context, task)) { - task = RunnableWrapper.wrapIfNeeded(task); ContextStore contextStore = InstrumentationContext.get(Runnable.class, PropagatedContext.class); return ExecutorAdviceHelper.attachContextToTask(context, contextStore, task); diff --git a/instrumentation/jetty/jetty-8.0/javaagent/src/test/groovy/QueuedThreadPoolTest.groovy b/instrumentation/jetty/jetty-8.0/javaagent/src/test/groovy/QueuedThreadPoolTest.groovy index de44416651..ec9d2cddb4 100644 --- a/instrumentation/jetty/jetty-8.0/javaagent/src/test/groovy/QueuedThreadPoolTest.groovy +++ b/instrumentation/jetty/jetty-8.0/javaagent/src/test/groovy/QueuedThreadPoolTest.groovy @@ -5,7 +5,6 @@ import io.opentelemetry.api.trace.SpanKind import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification -import io.opentelemetry.javaagent.instrumentation.jetty.JavaLambdaMaker import org.eclipse.jetty.util.thread.QueuedThreadPool import static org.junit.Assume.assumeTrue diff --git a/instrumentation/jetty/jetty-8.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/jetty/JavaLambdaMaker.java b/instrumentation/jetty/jetty-8.0/javaagent/src/test/java/JavaLambdaMaker.java similarity index 81% rename from instrumentation/jetty/jetty-8.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/jetty/JavaLambdaMaker.java rename to instrumentation/jetty/jetty-8.0/javaagent/src/test/java/JavaLambdaMaker.java index 6ef578e21b..b7c4840d2e 100644 --- a/instrumentation/jetty/jetty-8.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/jetty/JavaLambdaMaker.java +++ b/instrumentation/jetty/jetty-8.0/javaagent/src/test/java/JavaLambdaMaker.java @@ -3,8 +3,6 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.javaagent.instrumentation.jetty; - public class JavaLambdaMaker { @SuppressWarnings("FunctionalExpressionCanBeFolded") diff --git a/instrumentation/spring/spring-core-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/core/SimpleAsyncTaskExecutorInstrumentation.java b/instrumentation/spring/spring-core-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/core/SimpleAsyncTaskExecutorInstrumentation.java index d3e3350ba1..e35f345df2 100644 --- a/instrumentation/spring/spring-core-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/core/SimpleAsyncTaskExecutorInstrumentation.java +++ b/instrumentation/spring/spring-core-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/core/SimpleAsyncTaskExecutorInstrumentation.java @@ -19,7 +19,6 @@ import io.opentelemetry.javaagent.instrumentation.api.InstrumentationContext; import io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge; import io.opentelemetry.javaagent.instrumentation.api.concurrent.ExecutorAdviceHelper; import io.opentelemetry.javaagent.instrumentation.api.concurrent.PropagatedContext; -import io.opentelemetry.javaagent.instrumentation.api.concurrent.RunnableWrapper; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.matcher.ElementMatcher; @@ -50,7 +49,6 @@ public class SimpleAsyncTaskExecutorInstrumentation implements TypeInstrumentati @Advice.Argument(value = 0, readOnly = false) Runnable task) { Context context = Java8BytecodeBridge.currentContext(); if (ExecutorAdviceHelper.shouldPropagateContext(context, task)) { - task = RunnableWrapper.wrapIfNeeded(task); ContextStore contextStore = InstrumentationContext.get(Runnable.class, PropagatedContext.class); return ExecutorAdviceHelper.attachContextToTask(context, contextStore, task); diff --git a/javaagent-bootstrap/src/main/java/io/opentelemetry/javaagent/bootstrap/ClassFileTransformerHolder.java b/javaagent-bootstrap/src/main/java/io/opentelemetry/javaagent/bootstrap/ClassFileTransformerHolder.java new file mode 100644 index 0000000000..c4c22f444f --- /dev/null +++ b/javaagent-bootstrap/src/main/java/io/opentelemetry/javaagent/bootstrap/ClassFileTransformerHolder.java @@ -0,0 +1,28 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.bootstrap; + +import java.lang.instrument.ClassFileTransformer; + +/** + * Holder for {@link ClassFileTransformer} used by the instrumentation. Calling transform on this + * class file transformer processes given bytes the same way as they would be processed during + * loading of the class. + */ +public final class ClassFileTransformerHolder { + + private static volatile ClassFileTransformer classFileTransformer; + + public static ClassFileTransformer getClassFileTransformer() { + return classFileTransformer; + } + + public static void setClassFileTransformer(ClassFileTransformer transformer) { + classFileTransformer = transformer; + } + + private ClassFileTransformerHolder() {} +} diff --git a/javaagent-instrumentation-api/src/main/java/io/opentelemetry/javaagent/instrumentation/api/concurrent/CallableWrapper.java b/javaagent-instrumentation-api/src/main/java/io/opentelemetry/javaagent/instrumentation/api/concurrent/CallableWrapper.java deleted file mode 100644 index ff59597abd..0000000000 --- a/javaagent-instrumentation-api/src/main/java/io/opentelemetry/javaagent/instrumentation/api/concurrent/CallableWrapper.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.javaagent.instrumentation.api.concurrent; - -import java.util.concurrent.Callable; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * This is used to wrap lambda callables since currently we cannot instrument them. - * - *

FIXME: We should remove this once https://github.com/raphw/byte-buddy/issues/558 is fixed - */ -public final class CallableWrapper implements Callable { - - private static final Logger logger = LoggerFactory.getLogger(CallableWrapper.class); - - public static Callable wrapIfNeeded(Callable task) { - // We wrap only lambdas' anonymous classes and if given object has not already been wrapped. - // Anonymous classes have '/' in class name which is not allowed in 'normal' classes. - if (task.getClass().getName().contains("/") && !(task instanceof CallableWrapper)) { - logger.debug("Wrapping callable task {}", task); - return new CallableWrapper(task); - } - return task; - } - - private final Callable callable; - - private CallableWrapper(Callable callable) { - this.callable = callable; - } - - @Override - public T call() throws Exception { - return callable.call(); - } -} diff --git a/javaagent-instrumentation-api/src/main/java/io/opentelemetry/javaagent/instrumentation/api/concurrent/RunnableWrapper.java b/javaagent-instrumentation-api/src/main/java/io/opentelemetry/javaagent/instrumentation/api/concurrent/RunnableWrapper.java deleted file mode 100644 index b0985f72e7..0000000000 --- a/javaagent-instrumentation-api/src/main/java/io/opentelemetry/javaagent/instrumentation/api/concurrent/RunnableWrapper.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.javaagent.instrumentation.api.concurrent; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * This is used to wrap lambda runnables since currently we cannot instrument them. - * - *

FIXME: We should remove this once https://github.com/raphw/byte-buddy/issues/558 is fixed - */ -public final class RunnableWrapper implements Runnable { - - private static final Logger logger = LoggerFactory.getLogger(RunnableWrapper.class); - - public static Runnable wrapIfNeeded(Runnable task) { - // We wrap only lambdas' anonymous classes and if given object has not already been wrapped. - // Anonymous classes have '/' in class name which is not allowed in 'normal' classes. - if (task.getClass().getName().contains("/") && !(task instanceof RunnableWrapper)) { - logger.debug("Wrapping runnable task {}", task); - return new RunnableWrapper(task); - } - return task; - } - - private final Runnable runnable; - - private RunnableWrapper(Runnable runnable) { - this.runnable = runnable; - } - - @Override - public void run() { - runnable.run(); - } -} diff --git a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/AgentInstaller.java b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/AgentInstaller.java index 4acd8cdd78..7486e84177 100644 --- a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/AgentInstaller.java +++ b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/AgentInstaller.java @@ -16,6 +16,7 @@ import io.opentelemetry.context.ContextStorage; import io.opentelemetry.context.Scope; import io.opentelemetry.instrumentation.api.config.Config; import io.opentelemetry.javaagent.bootstrap.AgentClassLoader; +import io.opentelemetry.javaagent.bootstrap.ClassFileTransformerHolder; import io.opentelemetry.javaagent.extension.AgentExtension; import io.opentelemetry.javaagent.extension.AgentListener; import io.opentelemetry.javaagent.extension.bootstrap.BootstrapPackagesConfigurer; @@ -143,9 +144,6 @@ public class AgentInstaller { .with(AgentTooling.poolStrategy()) .with(new ClassLoadListener()) .with(AgentTooling.locationStrategy(Utils.getBootstrapProxy())); - // FIXME: we cannot enable it yet due to BB/JVM bug, see - // https://github.com/raphw/byte-buddy/issues/558 - // .with(AgentBuilder.LambdaInstrumentationStrategy.ENABLED) agentBuilder = configureIgnoredTypes(config, agentBuilder); @@ -178,6 +176,7 @@ public class AgentInstaller { logger.debug("Installed {} extension(s)", numberOfLoadedExtensions); ResettableClassFileTransformer resettableClassFileTransformer = agentBuilder.installOn(inst); + ClassFileTransformerHolder.setClassFileTransformer(resettableClassFileTransformer); runAfterAgentListeners(agentListeners, config); return resettableClassFileTransformer; } diff --git a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/ignore/AdditionalLibraryIgnoredTypesConfigurer.java b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/ignore/AdditionalLibraryIgnoredTypesConfigurer.java index 6ae3b350f1..d7b353dd22 100644 --- a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/ignore/AdditionalLibraryIgnoredTypesConfigurer.java +++ b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/ignore/AdditionalLibraryIgnoredTypesConfigurer.java @@ -76,7 +76,9 @@ public class AdditionalLibraryIgnoredTypesConfigurer implements IgnoredTypesConf .ignoreClass("org.springframework.data.") .allowClass("org.springframework.data.repository.core.support.RepositoryFactorySupport") .allowClass("org.springframework.data.convert.ClassGeneratingEntityInstantiator$") - .allowClass("org.springframework.data.jpa.repository.config.InspectionClassLoader"); + .allowClass("org.springframework.data.jpa.repository.config.InspectionClassLoader") + .allowClass( + "org.springframework.data.jpa.repository.query.QueryParameterSetter$NamedOrIndexedQueryParameterSetter$$Lambda$"); builder .ignoreClass("org.springframework.amqp.") @@ -96,7 +98,10 @@ public class AdditionalLibraryIgnoredTypesConfigurer implements IgnoredTypesConf .allowClass("org.springframework.boot.web.servlet.") .allowClass("org.springframework.boot.autoconfigure.BackgroundPreinitializer$") .allowClass("org.springframework.boot.autoconfigure.condition.OnClassCondition$") + .allowClass( + "org.springframework.boot.autoconfigure.web.ResourceProperties$Cache$Cachecontrol$$Lambda$") .allowClass("org.springframework.boot.web.embedded.netty.NettyWebServer$") + .allowClass("org.springframework.boot.web.embedded.tomcat.TomcatEmbeddedContext$$Lambda$") .allowClass( "org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainer$") .allowClass( @@ -127,6 +132,7 @@ public class AdditionalLibraryIgnoredTypesConfigurer implements IgnoredTypesConf // More runnables to deal with .allowClass("org.springframework.context.support.AbstractApplicationContext$") .allowClass("org.springframework.context.support.ContextTypeMatchClassLoader") + .allowClass("org.springframework.context.support.DefaultLifecycleProcessor$$Lambda$") // Allow instrumenting ApplicationContext implementations - to inject beans .allowClass("org.springframework.context.annotation.AnnotationConfigApplicationContext") .allowClass("org.springframework.context.support.AbstractApplicationContext") @@ -222,7 +228,8 @@ public class AdditionalLibraryIgnoredTypesConfigurer implements IgnoredTypesConf builder .ignoreClass("com.google.common.") .allowClass("com.google.common.util.concurrent.") - .allowClass("com.google.common.base.internal.Finalizer"); + .allowClass("com.google.common.base.internal.Finalizer") + .allowClass("com.google.common.base.Java8Usage$$Lambda$"); builder .ignoreClass("com.google.inject.") diff --git a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/ignore/GlobalIgnoredTypesConfigurer.java b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/ignore/GlobalIgnoredTypesConfigurer.java index d1ef701e34..8046f344c5 100644 --- a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/ignore/GlobalIgnoredTypesConfigurer.java +++ b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/ignore/GlobalIgnoredTypesConfigurer.java @@ -56,12 +56,7 @@ public class GlobalIgnoredTypesConfigurer implements IgnoredTypesConfigurer { // clojure builder.ignoreClass("clojure.").ignoreClass("$fn__"); - builder - .ignoreClass("io.opentelemetry.javaagent.") - // FIXME: We should remove this once - // https://github.com/raphw/byte-buddy/issues/558 is fixed - .allowClass("io.opentelemetry.javaagent.instrumentation.api.concurrent.RunnableWrapper") - .allowClass("io.opentelemetry.javaagent.instrumentation.api.concurrent.CallableWrapper"); + builder.ignoreClass("io.opentelemetry.javaagent."); builder .ignoreClass("java.") @@ -72,6 +67,7 @@ public class GlobalIgnoredTypesConfigurer implements IgnoredTypesConfigurer { .allowClass("java.util.concurrent.") .allowClass("java.lang.reflect.Proxy") .allowClass("java.lang.ClassLoader") + .allowClass("java.lang.invoke.InnerClassLambdaMetafactory") // Concurrent instrumentation modifies the structure of // Cleaner class incompatibly with java9+ modules. // Working around until a long-term fix for modules can be diff --git a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/ignore/IgnoredTypesMatcher.java b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/ignore/IgnoredTypesMatcher.java index 4da87667cb..f04a864df4 100644 --- a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/ignore/IgnoredTypesMatcher.java +++ b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/ignore/IgnoredTypesMatcher.java @@ -33,7 +33,7 @@ public class IgnoredTypesMatcher extends ElementMatcher.Junction.AbstractBase