From 39e25ae328f74d74f7682dcf3c98392e24bd8aef Mon Sep 17 00:00:00 2001 From: Felix Barnsteiner Date: Tue, 2 Jun 2020 13:46:40 +0200 Subject: [PATCH 01/35] Use non-inlined advices where possible --- .../apm/agent/bci/ElasticApmAgent.java | 15 +++- .../bci/bytebuddy/ErrorLoggingListener.java | 18 ++++ .../postprocessor/AssignToArgument.java | 41 +++++++++ .../AssignToArgumentPostProcessorFactory.java | 64 +++++++++++++ .../postprocessor/AssignToField.java | 75 ++++++++++++++++ .../AssignToFieldPostProcessorFactory.java | 89 +++++++++++++++++++ .../postprocessor/AssignToReturn.java | 39 ++++++++ .../AssignToReturnPostProcessorFactory.java | 63 +++++++++++++ .../bytebuddy/postprocessor/package-info.java | 28 ++++++ .../apm/agent/bci/InstrumentationTest.java | 55 ++++++++++-- .../api/AbstractSpanInstrumentation.java | 68 +++++++------- .../api/ElasticApmApiInstrumentation.java | 73 ++++++++------- .../plugin/api/LegacySpanInstrumentation.java | 49 +++++----- .../api/TransactionInstrumentation.java | 29 +++--- .../JmsMessageConsumerInstrumentation.java | 12 ++- ...onsumerRecordsIteratorInstrumentation.java | 10 ++- ...sumerRecordsRecordListInstrumentation.java | 10 ++- ...ConsumerRecordsRecordsInstrumentation.java | 10 ++- ...rrideClassLoaderLookupInstrumentation.java | 13 +-- .../okhttp/OkHttp3ClientInstrumentation.java | 10 +-- .../impl/ApmSpanBuilderInstrumentation.java | 12 +-- .../impl/ApmSpanInstrumentation.java | 14 +-- .../impl/ScopeManagerInstrumentation.java | 23 +++-- .../impl/SpanContextInstrumentation.java | 35 +++++--- .../CommonsExecAsyncInstrumentation.java | 10 +-- .../agent/servlet/AsyncInstrumentation.java | 12 ++- ...RequestStreamRecordingInstrumentation.java | 25 +++--- .../agent/servlet/AbstractServletTest.java | 4 +- pom.xml | 2 +- 29 files changed, 722 insertions(+), 186 deletions(-) create mode 100644 apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToArgument.java create mode 100644 apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToArgumentPostProcessorFactory.java create mode 100644 apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToField.java create mode 100644 apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToFieldPostProcessorFactory.java create mode 100644 apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToReturn.java create mode 100644 apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToReturnPostProcessorFactory.java create mode 100644 apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/package-info.java diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/ElasticApmAgent.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/ElasticApmAgent.java index 9928294d88..24a551f9c4 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/ElasticApmAgent.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/ElasticApmAgent.java @@ -25,6 +25,7 @@ package co.elastic.apm.agent.bci; import co.elastic.apm.agent.bci.bytebuddy.AnnotationValueOffsetMappingFactory; +import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToArgumentPostProcessorFactory; import co.elastic.apm.agent.bci.bytebuddy.ErrorLoggingListener; import co.elastic.apm.agent.bci.bytebuddy.FailSafeDeclaredMethodsCompiler; import co.elastic.apm.agent.bci.bytebuddy.MatcherTimer; @@ -32,6 +33,8 @@ import co.elastic.apm.agent.bci.bytebuddy.RootPackageCustomLocator; import co.elastic.apm.agent.bci.bytebuddy.SimpleMethodSignatureOffsetMappingFactory; import co.elastic.apm.agent.bci.bytebuddy.SoftlyReferencingTypePoolCache; +import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToFieldPostProcessorFactory; +import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToReturnPostProcessorFactory; import co.elastic.apm.agent.bci.methodmatching.MethodMatcher; import co.elastic.apm.agent.bci.methodmatching.TraceMethodInstrumentation; import co.elastic.apm.agent.configuration.CoreConfiguration; @@ -83,6 +86,7 @@ import static co.elastic.apm.agent.bci.bytebuddy.ClassLoaderNameMatcher.classLoaderWithName; import static co.elastic.apm.agent.bci.bytebuddy.ClassLoaderNameMatcher.isReflectionClassLoader; import static net.bytebuddy.asm.Advice.ExceptionHandler.Default.PRINTING; +import static net.bytebuddy.matcher.ElementMatchers.annotationType; import static net.bytebuddy.matcher.ElementMatchers.any; import static net.bytebuddy.matcher.ElementMatchers.is; import static net.bytebuddy.matcher.ElementMatchers.isInterface; @@ -298,6 +302,10 @@ public DynamicType.Builder transform(DynamicType.Builder builder, TypeDesc private static AgentBuilder.Transformer.ForAdvice getTransformer(final ElasticApmTracer tracer, final ElasticApmInstrumentation instrumentation, final Logger logger, final ElementMatcher methodMatcher) { Advice.WithCustomMapping withCustomMapping = Advice .withCustomMapping() + .with(new Advice.PostProcessor.Factory.Compound( + new AssignToArgumentPostProcessorFactory(), + new AssignToReturnPostProcessorFactory(), + new AssignToFieldPostProcessorFactory())) .bind(new SimpleMethodSignatureOffsetMappingFactory()) .bind(new AnnotationValueOffsetMappingFactory()); Advice.OffsetMapping.Factory offsetMapping = instrumentation.getOffsetMapping(); @@ -410,6 +418,9 @@ private static AgentBuilder getAgentBuilder(final ByteBuddy byteBuddy, final Cor } } + final List classesExcludedFromInstrumentation = coreConfiguration.getClassesExcludedFromInstrumentation(); + final List defaultClassesExcludedFromInstrumentation = coreConfiguration.getDefaultClassesExcludedFromInstrumentation(); + return new AgentBuilder.Default(byteBuddy) .with(RedefinitionStrategy.RETRANSFORMATION) // when runtime attaching, only retransform up to 100 classes at once and sleep 100ms in-between as retransformation causes a stop-the-world pause @@ -470,13 +481,13 @@ private static AgentBuilder getAgentBuilder(final ByteBuddy byteBuddy, final Cor .or(new ElementMatcher.Junction.AbstractBase() { @Override public boolean matches(TypeDescription target) { - return WildcardMatcher.anyMatch(coreConfiguration.getDefaultClassesExcludedFromInstrumentation(), target.getName()) != null; + return WildcardMatcher.anyMatch(defaultClassesExcludedFromInstrumentation, target.getName()) != null; } }) .or(new ElementMatcher.Junction.AbstractBase() { @Override public boolean matches(TypeDescription target) { - return WildcardMatcher.anyMatch(coreConfiguration.getClassesExcludedFromInstrumentation(), target.getName()) != null; + return WildcardMatcher.anyMatch(classesExcludedFromInstrumentation, target.getName()) != null; } }) .disableClassFormatChanges(); diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/ErrorLoggingListener.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/ErrorLoggingListener.java index 0994bd5bbf..c362c76404 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/ErrorLoggingListener.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/ErrorLoggingListener.java @@ -25,14 +25,32 @@ package co.elastic.apm.agent.bci.bytebuddy; import net.bytebuddy.agent.builder.AgentBuilder; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.dynamic.DynamicType; import net.bytebuddy.utility.JavaModule; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + public class ErrorLoggingListener extends AgentBuilder.Listener.Adapter { private static final Logger logger = LoggerFactory.getLogger(ErrorLoggingListener.class); + @Override + public void onTransformation(TypeDescription typeDescription, ClassLoader classLoader, JavaModule module, boolean loaded, DynamicType dynamicType) { + if (typeDescription.getName().equals("org.apache.activemq.ActiveMQMessageConsumer")) { + try { + Files.write(Paths.get("/Users/felixbarnsteiner/projects/github/elastic/apm-agent-java/ActiveMQMessageConsumer.class"), dynamicType.getBytes()); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + @Override public void onError(String typeName, ClassLoader classLoader, JavaModule module, boolean loaded, Throwable throwable) { if (throwable instanceof MinimumClassFileVersionValidator.UnsupportedClassFileVersionException) { diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToArgument.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToArgument.java new file mode 100644 index 0000000000..fe8f9c3650 --- /dev/null +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToArgument.java @@ -0,0 +1,41 @@ +/*- + * #%L + * Elastic APM Java agent + * %% + * Copyright (C) 2018 - 2020 Elastic and contributors + * %% + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * #L% + */ +package co.elastic.apm.agent.bci.bytebuddy.postprocessor; + +import net.bytebuddy.implementation.bytecode.assign.Assigner; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface AssignToArgument { + + int value(); + + Assigner.Typing typing() default Assigner.Typing.STATIC; +} diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToArgumentPostProcessorFactory.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToArgumentPostProcessorFactory.java new file mode 100644 index 0000000000..99c2ded174 --- /dev/null +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToArgumentPostProcessorFactory.java @@ -0,0 +1,64 @@ +/*- + * #%L + * Elastic APM Java agent + * %% + * Copyright (C) 2018 - 2020 Elastic and contributors + * %% + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * #L% + */ +package co.elastic.apm.agent.bci.bytebuddy.postprocessor; + +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.annotation.AnnotationList; +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.description.method.ParameterDescription; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.implementation.bytecode.StackManipulation; +import net.bytebuddy.implementation.bytecode.assign.Assigner; +import net.bytebuddy.implementation.bytecode.member.MethodVariableAccess; + +import static net.bytebuddy.matcher.ElementMatchers.annotationType; + +public class AssignToArgumentPostProcessorFactory implements Advice.PostProcessor.Factory { + @Override + public Advice.PostProcessor make(final MethodDescription.InDefinedShape adviceMethod, boolean exit) { + final AnnotationList annotations = adviceMethod.getDeclaredAnnotations().filter(annotationType(AssignToArgument.class)); + if (!annotations.isEmpty()) { + final AssignToArgument assignTo = annotations.getOnly().prepare(AssignToArgument.class).load(); + return new Advice.PostProcessor() { + @Override + public StackManipulation resolve(TypeDescription instrumentedType, MethodDescription instrumentedMethod, Assigner assigner, Advice.ArgumentHandler argumentHandler) { + final ParameterDescription param = instrumentedMethod.getParameters().get(assignTo.value()); + final StackManipulation assign = assigner.assign(adviceMethod.getReturnType(), param.getType(), assignTo.typing()); + if (!assign.isValid()) { + throw new IllegalStateException("Cannot assign " + adviceMethod.getReturnType() + " to " + param.getType()); + } + return new StackManipulation.Compound( + MethodVariableAccess.of(adviceMethod.getReturnType()).loadFrom(exit ? argumentHandler.exit() : argumentHandler.enter()), + assign, + MethodVariableAccess.store(param) + ); + } + + }; + } else { + return Advice.PostProcessor.NoOp.INSTANCE; + } + } +} diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToField.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToField.java new file mode 100644 index 0000000000..87e461511f --- /dev/null +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToField.java @@ -0,0 +1,75 @@ +/*- + * #%L + * Elastic APM Java agent + * %% + * Copyright (C) 2018 - 2020 Elastic and contributors + * %% + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * #L% + */ +package co.elastic.apm.agent.bci.bytebuddy.postprocessor; + +import net.bytebuddy.dynamic.TargetType; +import net.bytebuddy.implementation.bytecode.assign.Assigner; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface AssignToField { + /** + * Returns the name of the field. + * + * @return The name of the field. + */ + String value(); + + /** + * Returns the type that declares the field that should be mapped to the annotated parameter. If this property + * is set to {@code void}, the field is looked up implicitly within the instrumented class's class hierarchy. + * The value can also be set to {@link TargetType} in order to look up the type on the instrumented type. + * + * @return The type that declares the field, {@code void} if this type should be determined implicitly or + * {@link TargetType} for the instrumented type. + */ + Class declaringType() default Void.class; + + /** + *

+ * Indicates if it is possible to write to this parameter. If this property is set to {@code false}, the annotated + * type must be equal to the type declaring the instrumented method if the typing is not also set to {@link Assigner.Typing#DYNAMIC}. + * If this property is set to {@code true}, the annotated parameter can be any super type of the instrumented method's declaring type. + *

+ *

+ * Important: This property must be set to {@code true} if the advice method is not inlined. + *

+ * + * @return {@code true} if this parameter is read-only. + */ + boolean readOnly() default true; + + /** + * The typing that should be applied when assigning the field value. + * + * @return The typing to apply upon assignment. + */ + Assigner.Typing typing() default Assigner.Typing.STATIC; +} diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToFieldPostProcessorFactory.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToFieldPostProcessorFactory.java new file mode 100644 index 0000000000..15fd0816f1 --- /dev/null +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToFieldPostProcessorFactory.java @@ -0,0 +1,89 @@ +/*- + * #%L + * Elastic APM Java agent + * %% + * Copyright (C) 2018 - 2020 Elastic and contributors + * %% + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * #L% + */ +package co.elastic.apm.agent.bci.bytebuddy.postprocessor; + +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.annotation.AnnotationList; +import net.bytebuddy.description.field.FieldDescription; +import net.bytebuddy.description.field.FieldList; +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.description.method.ParameterDescription; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.dynamic.TargetType; +import net.bytebuddy.dynamic.scaffold.FieldLocator; +import net.bytebuddy.implementation.bytecode.StackManipulation; +import net.bytebuddy.implementation.bytecode.assign.Assigner; +import net.bytebuddy.implementation.bytecode.member.FieldAccess; +import net.bytebuddy.implementation.bytecode.member.MethodVariableAccess; + +import static net.bytebuddy.matcher.ElementMatchers.annotationType; +import static net.bytebuddy.matcher.ElementMatchers.named; + +public class AssignToFieldPostProcessorFactory implements Advice.PostProcessor.Factory { + @Override + public Advice.PostProcessor make(final MethodDescription.InDefinedShape adviceMethod, boolean exit) { + final AnnotationList annotations = adviceMethod.getDeclaredAnnotations().filter(annotationType(AssignToField.class)); + if (!annotations.isEmpty()) { + final AssignToField assignTo = annotations.getOnly().prepare(AssignToField.class).load(); + return new Advice.PostProcessor() { + @Override + public StackManipulation resolve(TypeDescription instrumentedType, MethodDescription instrumentedMethod, Assigner assigner, Advice.ArgumentHandler argumentHandler) { + final FieldDescription field = getFieldLocator(instrumentedType, assignTo).locate(assignTo.value()).getField(); + + if (!field.isStatic() && instrumentedMethod.isStatic()) { + throw new IllegalStateException("Cannot read non-static field " + field + " from static method " + instrumentedMethod); + } else if (instrumentedMethod.isConstructor() && !exit) { + throw new IllegalStateException("Cannot access non-static field before calling constructor: " + instrumentedMethod); + } + + final StackManipulation assign = assigner.assign(adviceMethod.getReturnType(), field.getType(), assignTo.typing()); + if (!assign.isValid()) { + throw new IllegalStateException("Cannot assign " + adviceMethod.getReturnType() + " to " + field.getType()); + } + return new StackManipulation.Compound( + MethodVariableAccess.of(adviceMethod.getReturnType()).loadFrom(exit ? argumentHandler.exit() : argumentHandler.enter()), + assign, + FieldAccess.forField(field).write() + ); + } + + }; + } else { + return Advice.PostProcessor.NoOp.INSTANCE; + } + } + + private static FieldLocator getFieldLocator(TypeDescription instrumentedType, AssignToField assignTo) { + if (assignTo.declaringType() == Void.class) { + return new FieldLocator.ForClassHierarchy(instrumentedType); + } else { + final TypeDescription declaringType = TypeDescription.ForLoadedType.of(assignTo.declaringType()); + if (!declaringType.represents(TargetType.class) && !instrumentedType.isAssignableTo(declaringType)) { + throw new IllegalStateException(declaringType + " is no super type of " + instrumentedType); + } + return new FieldLocator.ForExactType(declaringType); + } + } +} diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToReturn.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToReturn.java new file mode 100644 index 0000000000..9114ed4d81 --- /dev/null +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToReturn.java @@ -0,0 +1,39 @@ +/*- + * #%L + * Elastic APM Java agent + * %% + * Copyright (C) 2018 - 2020 Elastic and contributors + * %% + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * #L% + */ +package co.elastic.apm.agent.bci.bytebuddy.postprocessor; + +import net.bytebuddy.implementation.bytecode.assign.Assigner; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface AssignToReturn { + + Assigner.Typing typing() default Assigner.Typing.STATIC; +} diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToReturnPostProcessorFactory.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToReturnPostProcessorFactory.java new file mode 100644 index 0000000000..c29d4ccc97 --- /dev/null +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToReturnPostProcessorFactory.java @@ -0,0 +1,63 @@ +/*- + * #%L + * Elastic APM Java agent + * %% + * Copyright (C) 2018 - 2020 Elastic and contributors + * %% + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * #L% + */ +package co.elastic.apm.agent.bci.bytebuddy.postprocessor; + +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.annotation.AnnotationList; +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.description.method.ParameterDescription; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.implementation.bytecode.StackManipulation; +import net.bytebuddy.implementation.bytecode.assign.Assigner; +import net.bytebuddy.implementation.bytecode.member.MethodReturn; +import net.bytebuddy.implementation.bytecode.member.MethodVariableAccess; + +import static net.bytebuddy.matcher.ElementMatchers.annotationType; + +public class AssignToReturnPostProcessorFactory implements Advice.PostProcessor.Factory { + @Override + public Advice.PostProcessor make(final MethodDescription.InDefinedShape adviceMethod, boolean exit) { + final AnnotationList annotations = adviceMethod.getDeclaredAnnotations().filter(annotationType(AssignToReturn.class)); + if (!annotations.isEmpty()) { + final AssignToReturn assignTo = annotations.getOnly().prepare(AssignToReturn.class).load(); + return new Advice.PostProcessor() { + @Override + public StackManipulation resolve(TypeDescription instrumentedType, MethodDescription instrumentedMethod, Assigner assigner, Advice.ArgumentHandler argumentHandler) { + final StackManipulation assign = assigner.assign(adviceMethod.getReturnType(), instrumentedMethod.getReturnType(), assignTo.typing()); + if (!assign.isValid()) { + throw new IllegalStateException("Cannot assign " + adviceMethod.getReturnType() + " to " + instrumentedMethod.getReturnType()); + } + return new StackManipulation.Compound( + MethodVariableAccess.of(adviceMethod.getReturnType()).loadFrom(exit ? argumentHandler.exit() : argumentHandler.enter()), + assign, + MethodVariableAccess.of(instrumentedMethod.getReturnType()).storeAt(argumentHandler.returned()) + ); + } + }; + } else { + return Advice.PostProcessor.NoOp.INSTANCE; + } + } +} diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/package-info.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/package-info.java new file mode 100644 index 0000000000..1a873c0d7a --- /dev/null +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/package-info.java @@ -0,0 +1,28 @@ +/*- + * #%L + * Elastic APM Java agent + * %% + * Copyright (C) 2018 - 2020 Elastic and contributors + * %% + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * #L% + */ +@NonnullApi +package co.elastic.apm.agent.bci.bytebuddy.postprocessor; + +import co.elastic.apm.agent.annotation.NonnullApi; diff --git a/apm-agent-core/src/test/java/co/elastic/apm/agent/bci/InstrumentationTest.java b/apm-agent-core/src/test/java/co/elastic/apm/agent/bci/InstrumentationTest.java index cb7db0d505..c4e3cf91ab 100644 --- a/apm-agent-core/src/test/java/co/elastic/apm/agent/bci/InstrumentationTest.java +++ b/apm-agent-core/src/test/java/co/elastic/apm/agent/bci/InstrumentationTest.java @@ -25,6 +25,8 @@ package co.elastic.apm.agent.bci; import co.elastic.apm.agent.MockTracer; +import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToField; +import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToReturn; import co.elastic.apm.agent.configuration.CoreConfiguration; import co.elastic.apm.agent.configuration.SpyConfiguration; import co.elastic.apm.agent.impl.ElasticApmTracer; @@ -55,6 +57,7 @@ class InstrumentationTest { private ElasticApmTracer tracer; private ConfigurationRegistry configurationRegistry; private CoreConfiguration coreConfig; + private String privateString; @BeforeEach void setup() { @@ -74,6 +77,16 @@ void testIntercept() { assertThat(interceptMe()).isEqualTo("intercepted"); } + @Test + void testFieldAccess() { + init(configurationRegistry, List.of(new FieldAccessInstrumentation())); + assignToField("@AssignToField"); + assertThat(privateString).isEqualTo("@AssignToField"); + } + + public void assignToField(String s) { + } + @Test void testDisabled() { when(coreConfig.getDisabledInstrumentations()). @@ -225,9 +238,10 @@ private String interceptMe() { } public static class TestInstrumentation extends ElasticApmInstrumentation { - @Advice.OnMethodExit - public static void onMethodExit(@Advice.Return(readOnly = false) String returnValue) { - returnValue = "intercepted"; + @AssignToReturn + @Advice.OnMethodExit(inline = false) + public static String onMethodExit() { + return "intercepted"; } @Override @@ -247,9 +261,10 @@ public Collection getInstrumentationGroupNames() { } public static class MathInstrumentation extends ElasticApmInstrumentation { - @Advice.OnMethodExit - public static void onMethodExit(@Advice.Return(readOnly = false) int returnValue) { - returnValue = 42; + @AssignToReturn + @Advice.OnMethodExit(inline = false) + public static int onMethodExit() { + return 42; } @Override @@ -292,12 +307,13 @@ public Collection getInstrumentationGroupNames() { public static class SuppressExceptionInstrumentation extends ElasticApmInstrumentation { @Advice.OnMethodEnter(suppress = Throwable.class) - public static void onMethodEnter() { + public static String onMethodEnter() { throw new RuntimeException("This exception should be suppressed"); } + @AssignToReturn @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) - public static void onMethodExit(@Advice.Thrown Throwable throwable) { + public static String onMethodExit(@Advice.Thrown Throwable throwable) { throw new RuntimeException("This exception should be suppressed"); } @@ -316,4 +332,27 @@ public Collection getInstrumentationGroupNames() { return Collections.emptyList(); } } + + public static class FieldAccessInstrumentation extends ElasticApmInstrumentation { + + @AssignToField("privateString") + @Advice.OnMethodEnter + public static String onEnter(@Advice.Argument(0) String s) { + return s; + } + + @Override + public ElementMatcher getTypeMatcher() { + return ElementMatchers.named("co.elastic.apm.agent.bci.InstrumentationTest"); + } + @Override + public ElementMatcher getMethodMatcher() { + return ElementMatchers.named("assignToField"); + } + + @Override + public Collection getInstrumentationGroupNames() { + return List.of("test", "experimental"); + } + } } diff --git a/apm-agent-plugins/apm-api-plugin/src/main/java/co/elastic/apm/agent/plugin/api/AbstractSpanInstrumentation.java b/apm-agent-plugins/apm-api-plugin/src/main/java/co/elastic/apm/agent/plugin/api/AbstractSpanInstrumentation.java index c5a8c054ea..ea70d7f527 100644 --- a/apm-agent-plugins/apm-api-plugin/src/main/java/co/elastic/apm/agent/plugin/api/AbstractSpanInstrumentation.java +++ b/apm-agent-plugins/apm-api-plugin/src/main/java/co/elastic/apm/agent/plugin/api/AbstractSpanInstrumentation.java @@ -25,6 +25,7 @@ package co.elastic.apm.agent.plugin.api; import co.elastic.apm.agent.bci.VisibleForAdvice; +import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToReturn; import co.elastic.apm.agent.impl.transaction.AbstractSpan; import co.elastic.apm.agent.impl.transaction.Span; import co.elastic.apm.agent.impl.transaction.Transaction; @@ -69,7 +70,7 @@ public SetNameInstrumentation() { } @VisibleForAdvice - @Advice.OnMethodEnter(suppress = Throwable.class) + @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) public static void setName(@Advice.FieldValue(value = "span", typing = Assigner.Typing.DYNAMIC) AbstractSpan context, @Advice.Argument(0) String name) { context.withName(name, PRIO_USER_SUPPLIED); @@ -82,7 +83,7 @@ public SetTypeInstrumentation() { } @VisibleForAdvice - @Advice.OnMethodEnter(suppress = Throwable.class) + @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) public static void setType(@Advice.FieldValue(value = "span", typing = Assigner.Typing.DYNAMIC) AbstractSpan context, @Advice.Argument(0) String type) { if (context instanceof Transaction) { @@ -99,7 +100,7 @@ public SetTypesInstrumentation() { } @VisibleForAdvice - @Advice.OnMethodEnter(suppress = Throwable.class) + @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) public static void setType(@Advice.Argument(0) Object span, @Advice.Argument(1) @Nullable String type, @Advice.Argument(2) @Nullable String subtype, @@ -116,10 +117,10 @@ public DoCreateSpanInstrumentation() { } @VisibleForAdvice - @Advice.OnMethodExit(suppress = Throwable.class) - public static void doCreateSpan(@Advice.FieldValue(value = "span", typing = Assigner.Typing.DYNAMIC) AbstractSpan context, - @Advice.Return(readOnly = false) Object result) { - result = context.createSpan(); + @AssignToReturn + @Advice.OnMethodExit(suppress = Throwable.class, inline = false) + public static Span doCreateSpan(@Advice.FieldValue(value = "span", typing = Assigner.Typing.DYNAMIC) AbstractSpan context) { + return context.createSpan(); } } @@ -129,7 +130,7 @@ public SetStartTimestampInstrumentation() { } @VisibleForAdvice - @Advice.OnMethodEnter(suppress = Throwable.class) + @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) public static void setStartTimestamp(@Advice.FieldValue(value = "span", typing = Assigner.Typing.DYNAMIC) AbstractSpan context, @Advice.Argument(value = 0) long epochMicros) { context.setStartTimestamp(epochMicros); @@ -142,7 +143,7 @@ public EndInstrumentation() { } @VisibleForAdvice - @Advice.OnMethodEnter(suppress = Throwable.class) + @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) public static void end(@Advice.FieldValue(value = "span", typing = Assigner.Typing.DYNAMIC) AbstractSpan context) { context.end(); } @@ -154,7 +155,7 @@ public EndWithTimestampInstrumentation() { } @VisibleForAdvice - @Advice.OnMethodEnter(suppress = Throwable.class) + @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) public static void end(@Advice.FieldValue(value = "span", typing = Assigner.Typing.DYNAMIC) AbstractSpan context, @Advice.Argument(value = 0) long epochMicros) { context.end(epochMicros); @@ -171,12 +172,13 @@ public CaptureExceptionInstrumentation() { .and(returns(String.class))); } + @Nullable + @AssignToReturn @VisibleForAdvice - @Advice.OnMethodExit(suppress = Throwable.class) - public static void captureException(@Advice.FieldValue(value = "span", typing = Assigner.Typing.DYNAMIC) AbstractSpan context, - @Advice.Argument(0) Throwable t, - @Advice.Return(readOnly = false) String errorId) { - errorId = context.captureExceptionAndGetErrorId(t); + @Advice.OnMethodExit(suppress = Throwable.class, inline = false) + public static String captureException(@Advice.FieldValue(value = "span", typing = Assigner.Typing.DYNAMIC) AbstractSpan context, + @Advice.Argument(0) Throwable t) { + return context.captureExceptionAndGetErrorId(t); } } @@ -192,7 +194,7 @@ public LegacyCaptureExceptionInstrumentation() { } @VisibleForAdvice - @Advice.OnMethodExit(suppress = Throwable.class) + @Advice.OnMethodExit(suppress = Throwable.class, inline = false) public static void captureException(@Advice.FieldValue(value = "span", typing = Assigner.Typing.DYNAMIC) AbstractSpan context, @Advice.Argument(0) Throwable t) { context.captureException(t); @@ -204,11 +206,11 @@ public GetIdInstrumentation() { super(named("getId").and(takesArguments(0))); } + @AssignToReturn @VisibleForAdvice - @Advice.OnMethodExit(suppress = Throwable.class) - public static void getId(@Advice.FieldValue(value = "span", typing = Assigner.Typing.DYNAMIC) AbstractSpan context, - @Advice.Return(readOnly = false) String id) { - id = context.getTraceContext().getId().toString(); + @Advice.OnMethodExit(suppress = Throwable.class, inline = false) + public static String getId(@Advice.FieldValue(value = "span", typing = Assigner.Typing.DYNAMIC) AbstractSpan context) { + return context.getTraceContext().getId().toString(); } } @@ -217,11 +219,11 @@ public GetTraceIdInstrumentation() { super(named("getTraceId").and(takesArguments(0))); } + @AssignToReturn @VisibleForAdvice - @Advice.OnMethodExit(suppress = Throwable.class) - public static void getTraceId(@Advice.FieldValue(value = "span", typing = Assigner.Typing.DYNAMIC) AbstractSpan context, - @Advice.Return(readOnly = false) String traceId) { - traceId = context.getTraceContext().getTraceId().toString(); + @Advice.OnMethodExit(suppress = Throwable.class, inline = false) + public static String getTraceId(@Advice.FieldValue(value = "span", typing = Assigner.Typing.DYNAMIC) AbstractSpan context) { + return context.getTraceContext().getTraceId().toString(); } } @@ -231,7 +233,7 @@ public AddStringLabelInstrumentation() { } @VisibleForAdvice - @Advice.OnMethodEnter(suppress = Throwable.class) + @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) public static void addLabel(@Advice.FieldValue(value = "span", typing = Assigner.Typing.DYNAMIC) AbstractSpan context, @Advice.Argument(0) String key, @Nullable @Advice.Argument(1) String value) { if (value != null) { @@ -246,7 +248,7 @@ public AddNumberLabelInstrumentation() { } @VisibleForAdvice - @Advice.OnMethodEnter(suppress = Throwable.class) + @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) public static void addLabel(@Advice.FieldValue(value = "span", typing = Assigner.Typing.DYNAMIC) AbstractSpan context, @Advice.Argument(0) String key, @Nullable @Advice.Argument(1) Number value) { if (value != null) { @@ -261,7 +263,7 @@ public AddBooleanLabelInstrumentation() { } @VisibleForAdvice - @Advice.OnMethodEnter(suppress = Throwable.class) + @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) public static void addLabel(@Advice.FieldValue(value = "span", typing = Assigner.Typing.DYNAMIC) AbstractSpan context, @Advice.Argument(0) String key, @Nullable @Advice.Argument(1) Boolean value) { if (value != null) { @@ -276,7 +278,7 @@ public ActivateInstrumentation() { } @VisibleForAdvice - @Advice.OnMethodEnter(suppress = Throwable.class) + @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) public static void activate(@Advice.FieldValue(value = "span", typing = Assigner.Typing.DYNAMIC) AbstractSpan context) { context.activate(); } @@ -287,11 +289,11 @@ public IsSampledInstrumentation() { super(named("isSampled")); } + @AssignToReturn @VisibleForAdvice - @Advice.OnMethodExit(suppress = Throwable.class) - public static void isSampled(@Advice.FieldValue(value = "span", typing = Assigner.Typing.DYNAMIC) AbstractSpan context, - @Advice.Return(readOnly = false) boolean sampled) { - sampled = context.isSampled(); + @Advice.OnMethodExit(suppress = Throwable.class, inline = false) + public static boolean isSampled(@Advice.FieldValue(value = "span", typing = Assigner.Typing.DYNAMIC) AbstractSpan context) { + return context.isSampled(); } } @@ -302,7 +304,7 @@ public InjectTraceHeadersInstrumentation() { } @VisibleForAdvice - @Advice.OnMethodExit(suppress = Throwable.class) + @Advice.OnMethodExit(suppress = Throwable.class, inline = false) public static void injectTraceHeaders(@Advice.FieldValue(value = "span", typing = Assigner.Typing.DYNAMIC) AbstractSpan context, @Advice.Argument(0) MethodHandle addHeaderMethodHandle, @Advice.Argument(1) @Nullable Object headerInjector) throws Throwable { diff --git a/apm-agent-plugins/apm-api-plugin/src/main/java/co/elastic/apm/agent/plugin/api/ElasticApmApiInstrumentation.java b/apm-agent-plugins/apm-api-plugin/src/main/java/co/elastic/apm/agent/plugin/api/ElasticApmApiInstrumentation.java index 21f2159eea..aaa32d5830 100644 --- a/apm-agent-plugins/apm-api-plugin/src/main/java/co/elastic/apm/agent/plugin/api/ElasticApmApiInstrumentation.java +++ b/apm-agent-plugins/apm-api-plugin/src/main/java/co/elastic/apm/agent/plugin/api/ElasticApmApiInstrumentation.java @@ -25,6 +25,8 @@ package co.elastic.apm.agent.plugin.api; import co.elastic.apm.agent.bci.VisibleForAdvice; +import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToReturn; +import co.elastic.apm.agent.impl.transaction.Transaction; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.method.MethodDescription; import net.bytebuddy.description.type.TypeDescription; @@ -62,12 +64,15 @@ public StartTransactionInstrumentation() { super(named("doStartTransaction")); } + @Nullable + @AssignToReturn @VisibleForAdvice - @Advice.OnMethodExit(suppress = Throwable.class) - private static void doStartTransaction(@Advice.Origin Class clazz, @Advice.Return(readOnly = false) Object transaction) { + @Advice.OnMethodExit(suppress = Throwable.class, inline = false) + public static Object doStartTransaction(@Advice.Origin Class clazz) { if (tracer != null) { - transaction = tracer.startRootTransaction(clazz.getClassLoader()); + return tracer.startRootTransaction(clazz.getClassLoader()); } + return null; } } @@ -77,25 +82,27 @@ public StartTransactionWithRemoteParentInstrumentation() { super(named("doStartTransactionWithRemoteParentFunction")); } + @Nullable + @AssignToReturn @SuppressWarnings({"UnusedAssignment", "ParameterCanBeLocal", "unused"}) @VisibleForAdvice - @Advice.OnMethodExit(suppress = Throwable.class) - private static void doStartTransaction(@Advice.Origin Class clazz, - @Advice.Return(readOnly = false) Object transaction, - @Advice.Argument(0) MethodHandle getFirstHeader, - @Advice.Argument(1) @Nullable Object headerExtractor, - @Advice.Argument(2) MethodHandle getAllHeaders, - @Advice.Argument(3) @Nullable Object headersExtractor) { - if (tracer != null) { - if (headersExtractor != null) { - HeadersExtractorBridge headersExtractorBridge = HeadersExtractorBridge.get(getFirstHeader, getAllHeaders); - transaction = tracer.startChildTransaction(HeadersExtractorBridge.Extractor.of(headerExtractor, headersExtractor), headersExtractorBridge, clazz.getClassLoader()); - } else if (headerExtractor != null) { - HeaderExtractorBridge headersExtractorBridge = HeaderExtractorBridge.get(getFirstHeader); - transaction = tracer.startChildTransaction(headerExtractor, headersExtractorBridge, clazz.getClassLoader()); - } else { - transaction = tracer.startRootTransaction(clazz.getClassLoader()); - } + @Advice.OnMethodExit(suppress = Throwable.class, inline = false) + public static Transaction doStartTransaction(@Advice.Origin Class clazz, + @Advice.Argument(0) MethodHandle getFirstHeader, + @Advice.Argument(1) @Nullable Object headerExtractor, + @Advice.Argument(2) MethodHandle getAllHeaders, + @Advice.Argument(3) @Nullable Object headersExtractor) { + if (tracer == null) { + return null; + } + if (headersExtractor != null) { + HeadersExtractorBridge headersExtractorBridge = HeadersExtractorBridge.get(getFirstHeader, getAllHeaders); + return tracer.startChildTransaction(HeadersExtractorBridge.Extractor.of(headerExtractor, headersExtractor), headersExtractorBridge, clazz.getClassLoader()); + } else if (headerExtractor != null) { + HeaderExtractorBridge headersExtractorBridge = HeaderExtractorBridge.get(getFirstHeader); + return tracer.startChildTransaction(headerExtractor, headersExtractorBridge, clazz.getClassLoader()); + } else { + return tracer.startRootTransaction(clazz.getClassLoader()); } } } @@ -105,12 +112,15 @@ public CurrentTransactionInstrumentation() { super(named("doGetCurrentTransaction")); } + @Nullable + @AssignToReturn @VisibleForAdvice - @Advice.OnMethodExit(suppress = Throwable.class) - private static void doGetCurrentTransaction(@Advice.Return(readOnly = false) Object transaction) { - if (tracer != null) { - transaction = tracer.currentTransaction(); + @Advice.OnMethodExit(suppress = Throwable.class, inline = false) + public static Object doGetCurrentTransaction() { + if (tracer == null) { + return null; } + return tracer.currentTransaction(); } } @@ -119,12 +129,15 @@ public CurrentSpanInstrumentation() { super(named("doGetCurrentSpan")); } + @Nullable + @AssignToReturn @VisibleForAdvice - @Advice.OnMethodExit(suppress = Throwable.class) - private static void doGetCurrentSpan(@Advice.Return(readOnly = false) Object span) { - if (tracer != null) { - span = tracer.getActive(); + @Advice.OnMethodExit(suppress = Throwable.class, inline = false) + public static Object doGetCurrentSpan() { + if (tracer == null) { + return null; } + return tracer.getActive(); } } @@ -134,8 +147,8 @@ public CaptureExceptionInstrumentation() { } @VisibleForAdvice - @Advice.OnMethodEnter(suppress = Throwable.class) - private static void captureException(@Advice.Origin Class clazz, @Advice.Argument(0) @Nullable Throwable e) { + @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) + public static void captureException(@Advice.Origin Class clazz, @Advice.Argument(0) @Nullable Throwable e) { if (tracer != null) { tracer.captureAndReportException(e, clazz.getClassLoader()); } diff --git a/apm-agent-plugins/apm-api-plugin/src/main/java/co/elastic/apm/agent/plugin/api/LegacySpanInstrumentation.java b/apm-agent-plugins/apm-api-plugin/src/main/java/co/elastic/apm/agent/plugin/api/LegacySpanInstrumentation.java index 8fdc42e407..f8d98bcc5b 100644 --- a/apm-agent-plugins/apm-api-plugin/src/main/java/co/elastic/apm/agent/plugin/api/LegacySpanInstrumentation.java +++ b/apm-agent-plugins/apm-api-plugin/src/main/java/co/elastic/apm/agent/plugin/api/LegacySpanInstrumentation.java @@ -11,9 +11,9 @@ * the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -25,6 +25,7 @@ package co.elastic.apm.agent.plugin.api; import co.elastic.apm.agent.bci.VisibleForAdvice; +import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToReturn; import co.elastic.apm.agent.impl.transaction.AbstractSpan; import co.elastic.apm.agent.impl.transaction.Span; import net.bytebuddy.asm.Advice; @@ -33,6 +34,8 @@ import net.bytebuddy.implementation.bytecode.assign.Assigner; import net.bytebuddy.matcher.ElementMatcher; +import javax.annotation.Nullable; + import static co.elastic.apm.agent.impl.transaction.AbstractSpan.PRIO_USER_SUPPLIED; import static net.bytebuddy.matcher.ElementMatchers.hasSuperType; import static net.bytebuddy.matcher.ElementMatchers.named; @@ -99,11 +102,11 @@ public DoCreateSpanInstrumentation() { super(named("doCreateSpan")); } + @AssignToReturn @VisibleForAdvice - @Advice.OnMethodExit - public static void doCreateSpan(@Advice.FieldValue(value = "span", typing = Assigner.Typing.DYNAMIC) AbstractSpan span, - @Advice.Return(readOnly = false) Object result) { - result = span.createSpan(); + @Advice.OnMethodExit(inline = false) + public static Span doCreateSpan(@Advice.FieldValue(value = "span", typing = Assigner.Typing.DYNAMIC) AbstractSpan span) { + return span.createSpan(); } } @@ -126,7 +129,7 @@ public CaptureExceptionInstrumentation() { } @VisibleForAdvice - @Advice.OnMethodExit + @Advice.OnMethodExit(inline = false) public static void doCreateSpan(@Advice.FieldValue(value = "span", typing = Assigner.Typing.DYNAMIC) AbstractSpan span, @Advice.Argument(0) Throwable t) { span.captureException(t); @@ -138,13 +141,15 @@ public GetIdInstrumentation() { super(named("getId").and(takesArguments(0))); } + @Nullable + @AssignToReturn @VisibleForAdvice - @Advice.OnMethodExit - public static void getId(@Advice.FieldValue(value = "span", typing = Assigner.Typing.DYNAMIC) AbstractSpan span, - @Advice.Return(readOnly = false) String id) { - if (tracer != null) { - id = span.getTraceContext().getId().toString(); + @Advice.OnMethodExit(inline = false) + public static String getId(@Advice.FieldValue(value = "span", typing = Assigner.Typing.DYNAMIC) AbstractSpan span) { + if (tracer == null) { + return null; } + return span.getTraceContext().getId().toString(); } } @@ -153,13 +158,15 @@ public GetTraceIdInstrumentation() { super(named("getTraceId").and(takesArguments(0))); } + @Nullable + @AssignToReturn @VisibleForAdvice - @Advice.OnMethodExit - public static void getTraceId(@Advice.FieldValue(value = "span", typing = Assigner.Typing.DYNAMIC) AbstractSpan span, - @Advice.Return(readOnly = false) String traceId) { - if (tracer != null) { - traceId = span.getTraceContext().getTraceId().toString(); + @Advice.OnMethodExit(inline = false) + public static String getTraceId(@Advice.FieldValue(value = "span", typing = Assigner.Typing.DYNAMIC) AbstractSpan span) { + if (tracer == null) { + return null; } + return span.getTraceContext().getTraceId().toString(); } } @@ -193,11 +200,11 @@ public IsSampledInstrumentation() { super(named("isSampled")); } + @AssignToReturn @VisibleForAdvice - @Advice.OnMethodExit - public static void addTag(@Advice.FieldValue(value = "span", typing = Assigner.Typing.DYNAMIC) AbstractSpan span, - @Advice.Return(readOnly = false) boolean sampled) { - sampled = span.isSampled(); + @Advice.OnMethodExit(inline = false) + public static boolean addTag(@Advice.FieldValue(value = "span", typing = Assigner.Typing.DYNAMIC) AbstractSpan span) { + return span.isSampled(); } } } diff --git a/apm-agent-plugins/apm-api-plugin/src/main/java/co/elastic/apm/agent/plugin/api/TransactionInstrumentation.java b/apm-agent-plugins/apm-api-plugin/src/main/java/co/elastic/apm/agent/plugin/api/TransactionInstrumentation.java index f23c3817c3..11ed9a19cb 100644 --- a/apm-agent-plugins/apm-api-plugin/src/main/java/co/elastic/apm/agent/plugin/api/TransactionInstrumentation.java +++ b/apm-agent-plugins/apm-api-plugin/src/main/java/co/elastic/apm/agent/plugin/api/TransactionInstrumentation.java @@ -25,6 +25,7 @@ package co.elastic.apm.agent.plugin.api; import co.elastic.apm.agent.bci.VisibleForAdvice; +import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToReturn; import co.elastic.apm.agent.impl.transaction.TraceContext; import co.elastic.apm.agent.impl.transaction.Transaction; import net.bytebuddy.asm.Advice; @@ -64,7 +65,7 @@ public SetUserInstrumentation() { } @VisibleForAdvice - @Advice.OnMethodEnter(suppress = Throwable.class) + @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) public static void setUser(@Advice.FieldValue(value = "span", typing = Assigner.Typing.DYNAMIC) Transaction transaction, @Advice.Argument(0) String id, @Advice.Argument(1) String email, @Advice.Argument(2) String username) { transaction.setUser(id, email, username); @@ -76,17 +77,19 @@ public EnsureParentIdInstrumentation() { super(named("ensureParentId")); } + @Nullable + @AssignToReturn @VisibleForAdvice - @Advice.OnMethodExit(suppress = Throwable.class) - public static void ensureParentId(@Advice.FieldValue(value = "span", typing = Assigner.Typing.DYNAMIC) Transaction transaction, - @Advice.Return(readOnly = false) String spanId) { - if (tracer != null) { - final TraceContext traceContext = transaction.getTraceContext(); - if (traceContext.getParentId().isEmpty()) { - traceContext.getParentId().setToRandomValue(); - } - spanId = traceContext.getParentId().toString(); + @Advice.OnMethodExit(suppress = Throwable.class, inline = false) + public static String ensureParentId(@Advice.FieldValue(value = "span", typing = Assigner.Typing.DYNAMIC) Transaction transaction) { + if (tracer == null) { + return null; } + final TraceContext traceContext = transaction.getTraceContext(); + if (traceContext.getParentId().isEmpty()) { + traceContext.getParentId().setToRandomValue(); + } + return traceContext.getParentId().toString(); } } @@ -95,8 +98,8 @@ public SetResultInstrumentation() { super(named("setResult")); } - @Advice.OnMethodEnter(suppress = Throwable.class) - private static void ensureParentId(@Advice.FieldValue(value = "span", typing = Assigner.Typing.DYNAMIC) Transaction transaction, + @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) + public static void ensureParentId(@Advice.FieldValue(value = "span", typing = Assigner.Typing.DYNAMIC) Transaction transaction, @Advice.Argument(0) String result) { transaction.withResult(result); } @@ -108,7 +111,7 @@ public AddCustomContextInstrumentation() { } @VisibleForAdvice - @Advice.OnMethodEnter(suppress = Throwable.class) + @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) public static void addCustomContext(@Advice.FieldValue(value = "span", typing = Assigner.Typing.DYNAMIC) Transaction transaction, @Advice.Argument(0) String key, @Nullable @Advice.Argument(1) Object value) { if (value != null ) { diff --git a/apm-agent-plugins/apm-jms-plugin/src/main/java/co/elastic/apm/agent/jms/JmsMessageConsumerInstrumentation.java b/apm-agent-plugins/apm-jms-plugin/src/main/java/co/elastic/apm/agent/jms/JmsMessageConsumerInstrumentation.java index dc5b2ce007..8d2233b829 100644 --- a/apm-agent-plugins/apm-jms-plugin/src/main/java/co/elastic/apm/agent/jms/JmsMessageConsumerInstrumentation.java +++ b/apm-agent-plugins/apm-jms-plugin/src/main/java/co/elastic/apm/agent/jms/JmsMessageConsumerInstrumentation.java @@ -25,6 +25,8 @@ package co.elastic.apm.agent.jms; import co.elastic.apm.agent.bci.VisibleForAdvice; +import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToArgument; +import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToReturn; import co.elastic.apm.agent.configuration.MessagingConfiguration; import co.elastic.apm.agent.impl.ElasticApmTracer; import co.elastic.apm.agent.impl.transaction.AbstractSpan; @@ -261,15 +263,19 @@ public Class getAdviceClass() { public static class ListenerWrappingAdvice { - @Advice.OnMethodEnter(suppress = Throwable.class) - public static void beforeSetListener(@Advice.Argument(value = 0, readOnly = false) @Nullable MessageListener original) { + @Nullable + @AssignToArgument(0) + @Advice.OnMethodEnter(inline = false) + public static MessageListener beforeSetListener(@Advice.Argument(0) @Nullable MessageListener original) { //noinspection ConstantConditions - the Advice must be invoked only if the BaseJmsInstrumentation constructor was invoked JmsInstrumentationHelper helper = jmsInstrHelperManager.getForClassLoaderOfClass(MessageListener.class); if (helper != null) { - original = helper.wrapLambda(original); + return helper.wrapLambda(original); } + return original; } + } } } diff --git a/apm-agent-plugins/apm-kafka-plugin/apm-kafka-headers-plugin/src/main/java/co/elastic/apm/agent/kafka/ConsumerRecordsIteratorInstrumentation.java b/apm-agent-plugins/apm-kafka-plugin/apm-kafka-headers-plugin/src/main/java/co/elastic/apm/agent/kafka/ConsumerRecordsIteratorInstrumentation.java index 626be77a65..f8b8056d25 100644 --- a/apm-agent-plugins/apm-kafka-plugin/apm-kafka-headers-plugin/src/main/java/co/elastic/apm/agent/kafka/ConsumerRecordsIteratorInstrumentation.java +++ b/apm-agent-plugins/apm-kafka-plugin/apm-kafka-headers-plugin/src/main/java/co/elastic/apm/agent/kafka/ConsumerRecordsIteratorInstrumentation.java @@ -24,6 +24,7 @@ */ package co.elastic.apm.agent.kafka; +import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToReturn; import co.elastic.apm.agent.impl.ElasticApmTracer; import co.elastic.apm.agent.kafka.helper.KafkaInstrumentationHeadersHelper; import net.bytebuddy.asm.Advice; @@ -66,10 +67,12 @@ public Class getAdviceClass() { @SuppressWarnings("rawtypes") public static class ConsumerRecordsAdvice { - @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) - public static void wrapIterator(@Nullable @Advice.Return(readOnly = false) Iterator iterator) { + @Nullable + @AssignToReturn + @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class, inline = false) + public static Iterator wrapIterator(@Nullable @Advice.Return Iterator iterator) { if (tracer == null || !tracer.isRunning() || tracer.currentTransaction() != null) { - return; + return iterator; } //noinspection ConstantConditions,rawtypes @@ -78,6 +81,7 @@ public static void wrapIterator(@Nullable @Advice.Return(readOnly = false) Itera if (iterator != null && kafkaInstrumentationHelper != null) { iterator = kafkaInstrumentationHelper.wrapConsumerRecordIterator(iterator); } + return iterator; } } } diff --git a/apm-agent-plugins/apm-kafka-plugin/apm-kafka-headers-plugin/src/main/java/co/elastic/apm/agent/kafka/ConsumerRecordsRecordListInstrumentation.java b/apm-agent-plugins/apm-kafka-plugin/apm-kafka-headers-plugin/src/main/java/co/elastic/apm/agent/kafka/ConsumerRecordsRecordListInstrumentation.java index 7e234b3dbd..003f84d623 100644 --- a/apm-agent-plugins/apm-kafka-plugin/apm-kafka-headers-plugin/src/main/java/co/elastic/apm/agent/kafka/ConsumerRecordsRecordListInstrumentation.java +++ b/apm-agent-plugins/apm-kafka-plugin/apm-kafka-headers-plugin/src/main/java/co/elastic/apm/agent/kafka/ConsumerRecordsRecordListInstrumentation.java @@ -24,6 +24,7 @@ */ package co.elastic.apm.agent.kafka; +import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToReturn; import co.elastic.apm.agent.impl.ElasticApmTracer; import co.elastic.apm.agent.kafka.helper.KafkaInstrumentationHeadersHelper; import net.bytebuddy.asm.Advice; @@ -67,10 +68,12 @@ public Class getAdviceClass() { @SuppressWarnings("rawtypes") public static class ConsumerRecordsAdvice { - @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) - public static void wrapRecordList(@Nullable @Advice.Return(readOnly = false) List list) { + @Nullable + @AssignToReturn + @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class, inline = false) + public static List wrapRecordList(@Nullable @Advice.Return List list) { if (tracer == null || !tracer.isRunning() || tracer.currentTransaction() != null) { - return; + return list; } //noinspection ConstantConditions,rawtypes @@ -79,6 +82,7 @@ public static void wrapRecordList(@Nullable @Advice.Return(readOnly = false) Lis if (list != null && kafkaInstrumentationHelper != null) { list = kafkaInstrumentationHelper.wrapConsumerRecordList(list); } + return list; } } } diff --git a/apm-agent-plugins/apm-kafka-plugin/apm-kafka-headers-plugin/src/main/java/co/elastic/apm/agent/kafka/ConsumerRecordsRecordsInstrumentation.java b/apm-agent-plugins/apm-kafka-plugin/apm-kafka-headers-plugin/src/main/java/co/elastic/apm/agent/kafka/ConsumerRecordsRecordsInstrumentation.java index 043e304695..940cdcad1c 100644 --- a/apm-agent-plugins/apm-kafka-plugin/apm-kafka-headers-plugin/src/main/java/co/elastic/apm/agent/kafka/ConsumerRecordsRecordsInstrumentation.java +++ b/apm-agent-plugins/apm-kafka-plugin/apm-kafka-headers-plugin/src/main/java/co/elastic/apm/agent/kafka/ConsumerRecordsRecordsInstrumentation.java @@ -24,6 +24,7 @@ */ package co.elastic.apm.agent.kafka; +import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToReturn; import co.elastic.apm.agent.impl.ElasticApmTracer; import co.elastic.apm.agent.kafka.helper.KafkaInstrumentationHeadersHelper; import net.bytebuddy.asm.Advice; @@ -65,10 +66,12 @@ public Class getAdviceClass() { @SuppressWarnings("rawtypes") public static class ConsumerRecordsAdvice { - @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) - public static void wrapIterable(@Nullable @Advice.Return(readOnly = false) Iterable iterable) { + @Nullable + @AssignToReturn + @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class, inline = false) + public static Iterable wrapIterable(@Nullable @Advice.Return Iterable iterable) { if (tracer == null || !tracer.isRunning() || tracer.currentTransaction() != null) { - return; + return iterable; } //noinspection ConstantConditions,rawtypes @@ -77,6 +80,7 @@ public static void wrapIterable(@Nullable @Advice.Return(readOnly = false) Itera if (iterable != null && kafkaInstrumentationHelper != null) { iterable = kafkaInstrumentationHelper.wrapConsumerRecordIterable(iterable); } + return iterable; } } } diff --git a/apm-agent-plugins/apm-mule4-plugin/src/main/java/co/elastic/apm/agent/mule4/Mule4OverrideClassLoaderLookupInstrumentation.java b/apm-agent-plugins/apm-mule4-plugin/src/main/java/co/elastic/apm/agent/mule4/Mule4OverrideClassLoaderLookupInstrumentation.java index b6cba0648c..3af4d76037 100644 --- a/apm-agent-plugins/apm-mule4-plugin/src/main/java/co/elastic/apm/agent/mule4/Mule4OverrideClassLoaderLookupInstrumentation.java +++ b/apm-agent-plugins/apm-mule4-plugin/src/main/java/co/elastic/apm/agent/mule4/Mule4OverrideClassLoaderLookupInstrumentation.java @@ -11,9 +11,9 @@ * the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -25,6 +25,7 @@ package co.elastic.apm.agent.mule4; import co.elastic.apm.agent.bci.ElasticApmInstrumentation; +import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToReturn; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.NamedElement; import net.bytebuddy.description.method.MethodDescription; @@ -77,9 +78,10 @@ public Class getAdviceClass() { } public static class Mule4OverrideClassLoaderLookupAdvice { - @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) - public static void makeParentOnlyForAgentClasses(@Advice.Argument(0) @Nullable final String packageName, - @Advice.Return(readOnly = false) LookupStrategy lookupStrategy) { + @AssignToReturn + @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class, inline = false) + public static LookupStrategy makeParentOnlyForAgentClasses(@Advice.Argument(0) @Nullable final String packageName, + @Advice.Return LookupStrategy lookupStrategy) { // Until instrumentation mechanism is initiated, agent classes get loaded from the launching class loader. // Whenever flows are invoked with the visibility of this class loader's classpath, we can't let other agent @@ -90,6 +92,7 @@ public static void makeParentOnlyForAgentClasses(@Advice.Argument(0) @Nullable f Mule4OverrideClassLoaderLookupInstrumentation.class.getClassLoader() == null) { lookupStrategy = new DelegateOnlyLookupStrategy(ClassLoader.getSystemClassLoader()); } + return lookupStrategy; } } } diff --git a/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttp3ClientInstrumentation.java b/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttp3ClientInstrumentation.java index 8afae3a240..dd7a78523f 100644 --- a/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttp3ClientInstrumentation.java +++ b/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttp3ClientInstrumentation.java @@ -92,17 +92,17 @@ private static void onBeforeExecute( @Advice.FieldValue(value = "originalRequest @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) public static void onAfterExecute(@Advice.Return @Nullable okhttp3.Response response, - @Advice.Local("span") @Nullable Span span, @Advice.Thrown @Nullable Throwable t) { - if (span != null) { + final AbstractSpan active = getActive(); + if (active != null && active.isExit()) { try { if (response != null) { int statusCode = response.code(); - span.getContext().getHttp().withStatusCode(statusCode); + ((Span) active).getContext().getHttp().withStatusCode(statusCode); } - span.captureException(t); + active.captureException(t); } finally { - span.deactivate().end(); + active.deactivate().end(); } } } diff --git a/apm-agent-plugins/apm-opentracing-plugin/src/main/java/co/elastic/apm/agent/opentracing/impl/ApmSpanBuilderInstrumentation.java b/apm-agent-plugins/apm-opentracing-plugin/src/main/java/co/elastic/apm/agent/opentracing/impl/ApmSpanBuilderInstrumentation.java index 443edccbe8..03c7f9f0ff 100644 --- a/apm-agent-plugins/apm-opentracing-plugin/src/main/java/co/elastic/apm/agent/opentracing/impl/ApmSpanBuilderInstrumentation.java +++ b/apm-agent-plugins/apm-opentracing-plugin/src/main/java/co/elastic/apm/agent/opentracing/impl/ApmSpanBuilderInstrumentation.java @@ -25,6 +25,7 @@ package co.elastic.apm.agent.opentracing.impl; import co.elastic.apm.agent.bci.VisibleForAdvice; +import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToReturn; import co.elastic.apm.agent.impl.ElasticApmTracer; import co.elastic.apm.agent.impl.sampling.ConstantSampler; import co.elastic.apm.agent.impl.sampling.Sampler; @@ -69,15 +70,16 @@ public CreateSpanInstrumentation() { super(named("createSpan")); } - @Advice.OnMethodExit(suppress = Throwable.class) - public static void createSpan(@Advice.Argument(value = 0, typing = Assigner.Typing.DYNAMIC) @Nullable AbstractSpan parentContext, + @Nullable + @AssignToReturn + @Advice.OnMethodExit(suppress = Throwable.class, inline = false) + public static Object createSpan(@Advice.Argument(value = 0, typing = Assigner.Typing.DYNAMIC) @Nullable AbstractSpan parentContext, @Advice.Origin Class spanBuilderClass, @Advice.FieldValue(value = "tags") Map tags, @Advice.FieldValue(value = "operationName") String operationName, @Advice.FieldValue(value = "microseconds") long microseconds, - @Advice.Argument(1) @Nullable Iterable> baggage, - @Advice.Return(readOnly = false) Object span) { - span = doCreateTransactionOrSpan(parentContext, tags, operationName, microseconds, baggage, spanBuilderClass.getClassLoader()); + @Advice.Argument(1) @Nullable Iterable> baggage) { + return doCreateTransactionOrSpan(parentContext, tags, operationName, microseconds, baggage, spanBuilderClass.getClassLoader()); } @Nullable diff --git a/apm-agent-plugins/apm-opentracing-plugin/src/main/java/co/elastic/apm/agent/opentracing/impl/ApmSpanInstrumentation.java b/apm-agent-plugins/apm-opentracing-plugin/src/main/java/co/elastic/apm/agent/opentracing/impl/ApmSpanInstrumentation.java index 602dffedfc..22d716cc77 100644 --- a/apm-agent-plugins/apm-opentracing-plugin/src/main/java/co/elastic/apm/agent/opentracing/impl/ApmSpanInstrumentation.java +++ b/apm-agent-plugins/apm-opentracing-plugin/src/main/java/co/elastic/apm/agent/opentracing/impl/ApmSpanInstrumentation.java @@ -25,6 +25,7 @@ package co.elastic.apm.agent.opentracing.impl; import co.elastic.apm.agent.bci.VisibleForAdvice; +import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToReturn; import co.elastic.apm.agent.impl.context.web.ResultUtil; import co.elastic.apm.agent.impl.transaction.AbstractSpan; import co.elastic.apm.agent.impl.transaction.Span; @@ -71,7 +72,7 @@ public FinishInstrumentation() { } @Advice.OnMethodEnter(suppress = Throwable.class) - private static void finishInternal(@Advice.FieldValue(value = "dispatcher", readOnly = false, typing = Assigner.Typing.DYNAMIC) @Nullable AbstractSpan span, + private static void finishInternal(@Advice.FieldValue(value = "dispatcher", typing = Assigner.Typing.DYNAMIC) @Nullable AbstractSpan span, @Advice.Argument(0) long finishMicros) { if (span != null) { doFinishInternal(span, finishMicros); @@ -290,12 +291,11 @@ public GetTraceContextInstrumentation() { super(named("getTraceContext")); } - @Advice.OnMethodExit(suppress = Throwable.class) - public static void getTraceContext(@Advice.Argument(value = 0, typing = Assigner.Typing.DYNAMIC) @Nullable AbstractSpan abstractSpan, - @Advice.Return(readOnly = false) Object traceContext) { - if (abstractSpan != null) { - traceContext = abstractSpan; - } + @Nullable + @AssignToReturn + @Advice.OnMethodExit(suppress = Throwable.class, inline = false) + public static Object getTraceContext(@Advice.Argument(value = 0, typing = Assigner.Typing.DYNAMIC) @Nullable AbstractSpan abstractSpan) { + return abstractSpan; } } diff --git a/apm-agent-plugins/apm-opentracing-plugin/src/main/java/co/elastic/apm/agent/opentracing/impl/ScopeManagerInstrumentation.java b/apm-agent-plugins/apm-opentracing-plugin/src/main/java/co/elastic/apm/agent/opentracing/impl/ScopeManagerInstrumentation.java index 8dff44aaba..60dcea73ab 100644 --- a/apm-agent-plugins/apm-opentracing-plugin/src/main/java/co/elastic/apm/agent/opentracing/impl/ScopeManagerInstrumentation.java +++ b/apm-agent-plugins/apm-opentracing-plugin/src/main/java/co/elastic/apm/agent/opentracing/impl/ScopeManagerInstrumentation.java @@ -25,6 +25,7 @@ package co.elastic.apm.agent.opentracing.impl; import co.elastic.apm.agent.bci.VisibleForAdvice; +import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToReturn; import co.elastic.apm.agent.impl.transaction.AbstractSpan; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.method.MethodDescription; @@ -75,12 +76,15 @@ public CurrentSpanInstrumentation() { super(named("getCurrentSpan")); } + @Nullable + @AssignToReturn @VisibleForAdvice - @Advice.OnMethodExit(suppress = Throwable.class) - public static void getCurrentSpan(@Advice.Return(readOnly = false) Object span) { - if (tracer != null) { - span = tracer.getActive(); + @Advice.OnMethodExit(suppress = Throwable.class, inline = false) + public static Object getCurrentSpan() { + if (tracer == null) { + return null; } + return tracer.getActive(); } } @@ -91,12 +95,15 @@ public CurrentTraceContextInstrumentation() { super(named("getCurrentTraceContext")); } + @Nullable + @AssignToReturn @VisibleForAdvice - @Advice.OnMethodExit(suppress = Throwable.class) - public static void getCurrentTraceContext(@Advice.Return(readOnly = false) Object traceContext) { - if (tracer != null) { - traceContext = tracer.getActive(); + @Advice.OnMethodExit(suppress = Throwable.class, inline = false) + public static Object getCurrentTraceContext() { + if (tracer == null) { + return null; } + return tracer.getActive(); } } diff --git a/apm-agent-plugins/apm-opentracing-plugin/src/main/java/co/elastic/apm/agent/opentracing/impl/SpanContextInstrumentation.java b/apm-agent-plugins/apm-opentracing-plugin/src/main/java/co/elastic/apm/agent/opentracing/impl/SpanContextInstrumentation.java index 2430081bcf..6c3537e81b 100644 --- a/apm-agent-plugins/apm-opentracing-plugin/src/main/java/co/elastic/apm/agent/opentracing/impl/SpanContextInstrumentation.java +++ b/apm-agent-plugins/apm-opentracing-plugin/src/main/java/co/elastic/apm/agent/opentracing/impl/SpanContextInstrumentation.java @@ -25,6 +25,7 @@ package co.elastic.apm.agent.opentracing.impl; import co.elastic.apm.agent.bci.VisibleForAdvice; +import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToReturn; import co.elastic.apm.agent.impl.transaction.AbstractSpan; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.method.MethodDescription; @@ -69,13 +70,15 @@ public BaggageItemsInstrumentation() { } - @Advice.OnMethodExit(suppress = Throwable.class) - public static void baggageItems(@Advice.FieldValue(value = "traceContext", typing = Assigner.Typing.DYNAMIC) @Nullable AbstractSpan traceContext, - @Advice.Return(readOnly = false) Iterable> baggage) { + @Nullable + @AssignToReturn + @Advice.OnMethodExit(suppress = Throwable.class, inline = false) + public static Iterable> baggageItems(@Advice.FieldValue(value = "traceContext", typing = Assigner.Typing.DYNAMIC) @Nullable AbstractSpan traceContext) { if (traceContext != null) { - baggage = doGetBaggage(traceContext); + return doGetBaggage(traceContext); } else { logger.info("The traceContext is null"); + return null; } } @@ -93,12 +96,14 @@ public ToTraceIdInstrumentation() { super(named("toTraceId")); } - @Advice.OnMethodExit(suppress = Throwable.class) - public static void toTraceId(@Advice.FieldValue(value = "traceContext", typing = Assigner.Typing.DYNAMIC) @Nullable AbstractSpan traceContext, - @Advice.Return(readOnly = false) String traceId) { - if (traceContext != null) { - traceId = traceContext.getTraceContext().getTraceId().toString(); + @Nullable + @AssignToReturn + @Advice.OnMethodExit(suppress = Throwable.class, inline = false) + public static String toTraceId(@Advice.FieldValue(value = "traceContext", typing = Assigner.Typing.DYNAMIC) @Nullable AbstractSpan traceContext) { + if (traceContext == null) { + return null; } + return traceContext.getTraceContext().getTraceId().toString(); } } @@ -108,12 +113,14 @@ public ToSpanIdInstrumentation() { super(named("toSpanId")); } - @Advice.OnMethodExit(suppress = Throwable.class) - public static void toTraceId(@Advice.FieldValue(value = "traceContext", typing = Assigner.Typing.DYNAMIC) @Nullable AbstractSpan traceContext, - @Advice.Return(readOnly = false) String spanId) { - if (traceContext != null) { - spanId = traceContext.getTraceContext().getId().toString(); + @Nullable + @AssignToReturn + @Advice.OnMethodExit(suppress = Throwable.class, inline = false) + public static String toTraceId(@Advice.FieldValue(value = "traceContext", typing = Assigner.Typing.DYNAMIC) @Nullable AbstractSpan traceContext) { + if (traceContext == null) { + return null; } + return traceContext.getTraceContext().getId().toString(); } } } diff --git a/apm-agent-plugins/apm-process-plugin/src/main/java/co/elastic/apm/agent/process/CommonsExecAsyncInstrumentation.java b/apm-agent-plugins/apm-process-plugin/src/main/java/co/elastic/apm/agent/process/CommonsExecAsyncInstrumentation.java index 439eeb039a..4d03ae5909 100644 --- a/apm-agent-plugins/apm-process-plugin/src/main/java/co/elastic/apm/agent/process/CommonsExecAsyncInstrumentation.java +++ b/apm-agent-plugins/apm-process-plugin/src/main/java/co/elastic/apm/agent/process/CommonsExecAsyncInstrumentation.java @@ -25,6 +25,7 @@ package co.elastic.apm.agent.process; import co.elastic.apm.agent.bci.ElasticApmInstrumentation; +import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToArgument; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.NamedElement; import net.bytebuddy.description.method.MethodDescription; @@ -87,15 +88,14 @@ public Class getAdviceClass() { public static final class CommonsExecAdvice { + @AssignToArgument(0) @Advice.OnMethodEnter(suppress = Throwable.class) - private static void onEnter(@Advice.Argument(value = 0, readOnly = false) Runnable runnable) { + private static Runnable onEnter(Runnable runnable) { if (tracer == null || tracer.getActive() == null) { - return; + return runnable; } // context propagation is done by wrapping existing runnable argument - - //noinspection UnusedAssignment - runnable = tracer.getActive().withActive(runnable); + return tracer.getActive().withActive(runnable); } } } diff --git a/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/AsyncInstrumentation.java b/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/AsyncInstrumentation.java index 2261d60c30..1e717c74ca 100644 --- a/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/AsyncInstrumentation.java +++ b/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/AsyncInstrumentation.java @@ -26,6 +26,7 @@ import co.elastic.apm.agent.bci.HelperClassManager; import co.elastic.apm.agent.bci.VisibleForAdvice; +import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToArgument; import co.elastic.apm.agent.impl.ElasticApmTracer; import co.elastic.apm.agent.impl.transaction.Transaction; import net.bytebuddy.asm.Advice; @@ -179,8 +180,10 @@ public Class getAdviceClass() { @VisibleForAdvice public static class AsyncContextStartAdvice { - @Advice.OnMethodEnter(suppress = Throwable.class) - private static void onEnterAsyncContextStart(@Advice.Argument(value = 0, readOnly = false) @Nullable Runnable runnable) { + @Nullable + @AssignToArgument(0) + @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) + public static Runnable onEnterAsyncContextStart(@Advice.Argument(0) @Nullable Runnable runnable) { if (tracer != null && runnable != null && tracer.isWrappingAllowedOnThread()) { final Transaction transaction = tracer.currentTransaction(); if (transaction != null) { @@ -188,10 +191,11 @@ private static void onEnterAsyncContextStart(@Advice.Argument(value = 0, readOnl tracer.avoidWrappingOnThread(); } } + return runnable; } - @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Exception.class) - private static void onExitAsyncContextStart() { + @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Exception.class, inline = false) + public static void onExitAsyncContextStart() { if (tracer != null) { tracer.allowWrappingOnThread(); } diff --git a/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/RequestStreamRecordingInstrumentation.java b/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/RequestStreamRecordingInstrumentation.java index e20846c0b6..f4489736af 100644 --- a/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/RequestStreamRecordingInstrumentation.java +++ b/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/RequestStreamRecordingInstrumentation.java @@ -26,6 +26,7 @@ import co.elastic.apm.agent.bci.HelperClassManager; import co.elastic.apm.agent.bci.VisibleForAdvice; +import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToReturn; import co.elastic.apm.agent.impl.ElasticApmTracer; import co.elastic.apm.agent.impl.context.Request; import co.elastic.apm.agent.impl.transaction.Transaction; @@ -105,25 +106,27 @@ protected Boolean initialValue() { } }; - @Advice.OnMethodEnter(suppress = Throwable.class) - public static void onReadEnter(@Advice.This Object thiz, - @Advice.Local("transaction") Transaction transaction, - @Advice.Local("nested") boolean nested) { - nested = nestedThreadLocal.get(); + @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) + public static boolean onReadEnter(@Advice.This Object thiz) { + boolean nested = nestedThreadLocal.get(); nestedThreadLocal.set(Boolean.TRUE); + return nested; } - @Advice.OnMethodExit(suppress = Throwable.class) - public static void afterGetInputStream(@Advice.Return(readOnly = false) ServletInputStream inputStream, - @Advice.Local("nested") boolean nested) { - if (nested || tracer == null || wrapperHelperClassManager == null) { - return; + @AssignToReturn + @Advice.OnMethodExit(suppress = Throwable.class, inline = false) + public static ServletInputStream afterGetInputStream(@Advice.Return ServletInputStream inputStream, + @Advice.Enter boolean nested) { + if (nested == Boolean.TRUE || tracer == null || wrapperHelperClassManager == null) { + return inputStream; } try { final Transaction transaction = tracer.currentTransaction(); // only wrap if the body buffer has been initialized via ServletTransactionHelper.startCaptureBody if (transaction != null && transaction.getContext().getRequest().getBodyBuffer() != null) { - inputStream = wrapperHelperClassManager.getForClassLoaderOfClass(inputStream.getClass()).wrap(transaction.getContext().getRequest(), inputStream); + return wrapperHelperClassManager.getForClassLoaderOfClass(inputStream.getClass()).wrap(transaction.getContext().getRequest(), inputStream); + } else { + return inputStream; } } finally { nestedThreadLocal.set(Boolean.FALSE); diff --git a/apm-agent-plugins/apm-servlet-plugin/src/test/java/co/elastic/apm/agent/servlet/AbstractServletTest.java b/apm-agent-plugins/apm-servlet-plugin/src/test/java/co/elastic/apm/agent/servlet/AbstractServletTest.java index 18a6f3d05c..574587e1cd 100644 --- a/apm-agent-plugins/apm-servlet-plugin/src/test/java/co/elastic/apm/agent/servlet/AbstractServletTest.java +++ b/apm-agent-plugins/apm-servlet-plugin/src/test/java/co/elastic/apm/agent/servlet/AbstractServletTest.java @@ -68,8 +68,8 @@ void initServerAndClient() throws Exception { httpClient = new OkHttpClient.Builder() // set to 0 for debugging - .readTimeout(10, TimeUnit.SECONDS) - .connectTimeout(10, TimeUnit.SECONDS) + .readTimeout(0, TimeUnit.SECONDS) + .connectTimeout(0, TimeUnit.SECONDS) .retryOnConnectionFailure(false) .build(); } diff --git a/pom.xml b/pom.xml index dd7e7e7ad4..ed9d02d277 100644 --- a/pom.xml +++ b/pom.xml @@ -96,7 +96,7 @@ 5.0.15.RELEASE 9.4.11.v20180605 0.1.19 - 1.10.8 + 1.10.11-SNAPSHOT 1.17 From aaf059ca7474c72bf201f39d829aa6b0b79eea31 Mon Sep 17 00:00:00 2001 From: Felix Barnsteiner Date: Tue, 2 Jun 2020 14:35:41 +0200 Subject: [PATCH 02/35] Isolated @AssignToArgument test --- .../bci/bytebuddy/ErrorLoggingListener.java | 11 ------ .../apm/agent/bci/InstrumentationTest.java | 34 +++++++++++++++++++ 2 files changed, 34 insertions(+), 11 deletions(-) diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/ErrorLoggingListener.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/ErrorLoggingListener.java index c362c76404..71a3da1011 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/ErrorLoggingListener.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/ErrorLoggingListener.java @@ -40,17 +40,6 @@ public class ErrorLoggingListener extends AgentBuilder.Listener.Adapter { private static final Logger logger = LoggerFactory.getLogger(ErrorLoggingListener.class); - @Override - public void onTransformation(TypeDescription typeDescription, ClassLoader classLoader, JavaModule module, boolean loaded, DynamicType dynamicType) { - if (typeDescription.getName().equals("org.apache.activemq.ActiveMQMessageConsumer")) { - try { - Files.write(Paths.get("/Users/felixbarnsteiner/projects/github/elastic/apm-agent-java/ActiveMQMessageConsumer.class"), dynamicType.getBytes()); - } catch (Exception e) { - e.printStackTrace(); - } - } - } - @Override public void onError(String typeName, ClassLoader classLoader, JavaModule module, boolean loaded, Throwable throwable) { if (throwable instanceof MinimumClassFileVersionValidator.UnsupportedClassFileVersionException) { diff --git a/apm-agent-core/src/test/java/co/elastic/apm/agent/bci/InstrumentationTest.java b/apm-agent-core/src/test/java/co/elastic/apm/agent/bci/InstrumentationTest.java index c4e3cf91ab..a8318edc43 100644 --- a/apm-agent-core/src/test/java/co/elastic/apm/agent/bci/InstrumentationTest.java +++ b/apm-agent-core/src/test/java/co/elastic/apm/agent/bci/InstrumentationTest.java @@ -25,6 +25,7 @@ package co.elastic.apm.agent.bci; import co.elastic.apm.agent.MockTracer; +import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToArgument; import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToField; import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToReturn; import co.elastic.apm.agent.configuration.CoreConfiguration; @@ -87,6 +88,16 @@ void testFieldAccess() { public void assignToField(String s) { } + @Test + void testAssignToArgument() { + init(configurationRegistry, List.of(new AssignToArgumentInstrumentation())); + assertThat(assignToArgument("foo")).isEqualTo("foo@AssignToArgument"); + } + + public String assignToArgument(String s) { + return s; + } + @Test void testDisabled() { when(coreConfig.getDisabledInstrumentations()). @@ -355,4 +366,27 @@ public Collection getInstrumentationGroupNames() { return List.of("test", "experimental"); } } + + public static class AssignToArgumentInstrumentation extends ElasticApmInstrumentation { + + @AssignToArgument(0) + @Advice.OnMethodEnter + public static String onEnter(@Advice.Argument(0) String s) { + return s + "@AssignToArgument"; + } + + @Override + public ElementMatcher getTypeMatcher() { + return ElementMatchers.named("co.elastic.apm.agent.bci.InstrumentationTest"); + } + @Override + public ElementMatcher getMethodMatcher() { + return ElementMatchers.named("assignToArgument"); + } + + @Override + public Collection getInstrumentationGroupNames() { + return List.of("test", "experimental"); + } + } } From dc2955a7c79af74154502e67594933a91c1efceb Mon Sep 17 00:00:00 2001 From: Felix Barnsteiner Date: Tue, 2 Jun 2020 17:40:55 +0200 Subject: [PATCH 03/35] Fix AssignToFieldPostProcessorFactory by loading this on stack before writing to field --- .../postprocessor/AssignToFieldPostProcessorFactory.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToFieldPostProcessorFactory.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToFieldPostProcessorFactory.java index 15fd0816f1..a214997a45 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToFieldPostProcessorFactory.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToFieldPostProcessorFactory.java @@ -27,9 +27,7 @@ import net.bytebuddy.asm.Advice; import net.bytebuddy.description.annotation.AnnotationList; import net.bytebuddy.description.field.FieldDescription; -import net.bytebuddy.description.field.FieldList; import net.bytebuddy.description.method.MethodDescription; -import net.bytebuddy.description.method.ParameterDescription; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.dynamic.TargetType; import net.bytebuddy.dynamic.scaffold.FieldLocator; @@ -39,7 +37,6 @@ import net.bytebuddy.implementation.bytecode.member.MethodVariableAccess; import static net.bytebuddy.matcher.ElementMatchers.annotationType; -import static net.bytebuddy.matcher.ElementMatchers.named; public class AssignToFieldPostProcessorFactory implements Advice.PostProcessor.Factory { @Override @@ -63,6 +60,7 @@ public StackManipulation resolve(TypeDescription instrumentedType, MethodDescrip throw new IllegalStateException("Cannot assign " + adviceMethod.getReturnType() + " to " + field.getType()); } return new StackManipulation.Compound( + MethodVariableAccess.loadThis(), MethodVariableAccess.of(adviceMethod.getReturnType()).loadFrom(exit ? argumentHandler.exit() : argumentHandler.enter()), assign, FieldAccess.forField(field).write() From ed3109fe1585455f21c2b2e3bf17154825de38b3 Mon Sep 17 00:00:00 2001 From: Felix Barnsteiner Date: Tue, 2 Jun 2020 17:50:25 +0200 Subject: [PATCH 04/35] Add workaround for VerifyError --- .../apm/agent/jms/JmsMessageConsumerInstrumentation.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/apm-agent-plugins/apm-jms-plugin/src/main/java/co/elastic/apm/agent/jms/JmsMessageConsumerInstrumentation.java b/apm-agent-plugins/apm-jms-plugin/src/main/java/co/elastic/apm/agent/jms/JmsMessageConsumerInstrumentation.java index 8d2233b829..1be142a74e 100644 --- a/apm-agent-plugins/apm-jms-plugin/src/main/java/co/elastic/apm/agent/jms/JmsMessageConsumerInstrumentation.java +++ b/apm-agent-plugins/apm-jms-plugin/src/main/java/co/elastic/apm/agent/jms/JmsMessageConsumerInstrumentation.java @@ -276,6 +276,11 @@ public static MessageListener beforeSetListener(@Advice.Argument(0) @Nullable Me return original; } + @Advice.OnMethodExit + private static void onExit() { + // workaround for https://github.com/raphw/byte-buddy/issues/874#issuecomment-637616829 + } + } } } From 24614beffc0cbd205c7213d28edc96696a56134f Mon Sep 17 00:00:00 2001 From: Felix Barnsteiner Date: Tue, 2 Jun 2020 18:41:49 +0200 Subject: [PATCH 05/35] Fix compile errors and tests --- .../postprocessor/AssignToArgumentPostProcessorFactory.java | 2 +- .../postprocessor/AssignToFieldPostProcessorFactory.java | 2 +- .../postprocessor/AssignToReturnPostProcessorFactory.java | 4 +--- .../java/co/elastic/apm/agent/bci/InstrumentationTest.java | 2 ++ .../apm-redis-plugin/apm-redisson-plugin/pom.xml | 6 ++++++ 5 files changed, 11 insertions(+), 5 deletions(-) diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToArgumentPostProcessorFactory.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToArgumentPostProcessorFactory.java index 99c2ded174..0bcecce540 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToArgumentPostProcessorFactory.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToArgumentPostProcessorFactory.java @@ -37,7 +37,7 @@ public class AssignToArgumentPostProcessorFactory implements Advice.PostProcessor.Factory { @Override - public Advice.PostProcessor make(final MethodDescription.InDefinedShape adviceMethod, boolean exit) { + public Advice.PostProcessor make(final MethodDescription.InDefinedShape adviceMethod, final boolean exit) { final AnnotationList annotations = adviceMethod.getDeclaredAnnotations().filter(annotationType(AssignToArgument.class)); if (!annotations.isEmpty()) { final AssignToArgument assignTo = annotations.getOnly().prepare(AssignToArgument.class).load(); diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToFieldPostProcessorFactory.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToFieldPostProcessorFactory.java index a214997a45..def042d434 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToFieldPostProcessorFactory.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToFieldPostProcessorFactory.java @@ -40,7 +40,7 @@ public class AssignToFieldPostProcessorFactory implements Advice.PostProcessor.Factory { @Override - public Advice.PostProcessor make(final MethodDescription.InDefinedShape adviceMethod, boolean exit) { + public Advice.PostProcessor make(final MethodDescription.InDefinedShape adviceMethod, final boolean exit) { final AnnotationList annotations = adviceMethod.getDeclaredAnnotations().filter(annotationType(AssignToField.class)); if (!annotations.isEmpty()) { final AssignToField assignTo = annotations.getOnly().prepare(AssignToField.class).load(); diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToReturnPostProcessorFactory.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToReturnPostProcessorFactory.java index c29d4ccc97..35ccd4af7f 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToReturnPostProcessorFactory.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToReturnPostProcessorFactory.java @@ -27,18 +27,16 @@ import net.bytebuddy.asm.Advice; import net.bytebuddy.description.annotation.AnnotationList; import net.bytebuddy.description.method.MethodDescription; -import net.bytebuddy.description.method.ParameterDescription; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.implementation.bytecode.StackManipulation; import net.bytebuddy.implementation.bytecode.assign.Assigner; -import net.bytebuddy.implementation.bytecode.member.MethodReturn; import net.bytebuddy.implementation.bytecode.member.MethodVariableAccess; import static net.bytebuddy.matcher.ElementMatchers.annotationType; public class AssignToReturnPostProcessorFactory implements Advice.PostProcessor.Factory { @Override - public Advice.PostProcessor make(final MethodDescription.InDefinedShape adviceMethod, boolean exit) { + public Advice.PostProcessor make(final MethodDescription.InDefinedShape adviceMethod, final boolean exit) { final AnnotationList annotations = adviceMethod.getDeclaredAnnotations().filter(annotationType(AssignToReturn.class)); if (!annotations.isEmpty()) { final AssignToReturn assignTo = annotations.getOnly().prepare(AssignToReturn.class).load(); diff --git a/apm-agent-core/src/test/java/co/elastic/apm/agent/bci/InstrumentationTest.java b/apm-agent-core/src/test/java/co/elastic/apm/agent/bci/InstrumentationTest.java index a8318edc43..c92a7f32c9 100644 --- a/apm-agent-core/src/test/java/co/elastic/apm/agent/bci/InstrumentationTest.java +++ b/apm-agent-core/src/test/java/co/elastic/apm/agent/bci/InstrumentationTest.java @@ -42,6 +42,7 @@ import org.apache.commons.math.util.MathUtils; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.stagemonitor.configuration.ConfigurationRegistry; @@ -205,6 +206,7 @@ void testDontInstrumentOldClassFileVersions() { } @Test + @Disabled("this is currently a limitation in Byte Buddy") void testSuppressException() { ElasticApmAgent.initInstrumentation(tracer, ByteBuddyAgent.install(), diff --git a/apm-agent-plugins/apm-redis-plugin/apm-redisson-plugin/pom.xml b/apm-agent-plugins/apm-redis-plugin/apm-redisson-plugin/pom.xml index bb02da0325..e1c00fb9c8 100644 --- a/apm-agent-plugins/apm-redis-plugin/apm-redisson-plugin/pom.xml +++ b/apm-agent-plugins/apm-redis-plugin/apm-redisson-plugin/pom.xml @@ -34,6 +34,12 @@ redisson 3.12.3 provided + + + net.bytebuddy + byte-buddy + + From c5e92f9414a6e35acec8f22a86b5caaebf1b15ed Mon Sep 17 00:00:00 2001 From: Felix Barnsteiner Date: Wed, 3 Jun 2020 16:38:45 +0200 Subject: [PATCH 06/35] Add ability to assign to multiple arguments, fields and return value Migrate more plugins --- .../apm/agent/bci/ElasticApmAgent.java | 10 +- .../agent/bci/ElasticApmInstrumentation.java | 23 ++ .../bci/bytebuddy/postprocessor/AssignTo.java | 14 ++ .../postprocessor/AssignToArgument.java | 6 +- .../AssignToArgumentPostProcessorFactory.java | 64 ----- .../postprocessor/AssignToField.java | 16 +- .../AssignToPostProcessorFactory.java | 232 ++++++++++++++++++ .../postprocessor/AssignToReturn.java | 6 +- .../agent/util/RemoveOnGetThreadLocal.java | 22 ++ .../apm/agent/bci/InstrumentationTest.java | 107 ++++++++ .../ApacheHttpAsyncClientInstrumentation.java | 96 ++++---- ...asticsearchClientAsyncInstrumentation.java | 46 ++-- ...lasticsearchClientSyncInstrumentation.java | 31 +-- ...asticsearchClientAsyncInstrumentation.java | 42 ++-- ...lasticsearchClientSyncInstrumentation.java | 28 ++- .../kafka/KafkaProducerInstrumentation.java | 35 ++- .../KafkaProducerHeadersInstrumentation.java | 6 +- .../OkHttp3ClientAsyncInstrumentation.java | 29 ++- .../okhttp/OkHttp3ClientInstrumentation.java | 57 ++--- .../OkHttpClientAsyncInstrumentation.java | 29 ++- .../okhttp/OkHttpClientInstrumentation.java | 51 ++-- .../ExternalSpanContextInstrumentation.java | 56 +++-- 22 files changed, 679 insertions(+), 327 deletions(-) create mode 100644 apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignTo.java delete mode 100644 apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToArgumentPostProcessorFactory.java create mode 100644 apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToPostProcessorFactory.java create mode 100644 apm-agent-core/src/main/java/co/elastic/apm/agent/util/RemoveOnGetThreadLocal.java diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/ElasticApmAgent.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/ElasticApmAgent.java index 24a551f9c4..817ba58012 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/ElasticApmAgent.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/ElasticApmAgent.java @@ -25,7 +25,6 @@ package co.elastic.apm.agent.bci; import co.elastic.apm.agent.bci.bytebuddy.AnnotationValueOffsetMappingFactory; -import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToArgumentPostProcessorFactory; import co.elastic.apm.agent.bci.bytebuddy.ErrorLoggingListener; import co.elastic.apm.agent.bci.bytebuddy.FailSafeDeclaredMethodsCompiler; import co.elastic.apm.agent.bci.bytebuddy.MatcherTimer; @@ -33,8 +32,7 @@ import co.elastic.apm.agent.bci.bytebuddy.RootPackageCustomLocator; import co.elastic.apm.agent.bci.bytebuddy.SimpleMethodSignatureOffsetMappingFactory; import co.elastic.apm.agent.bci.bytebuddy.SoftlyReferencingTypePoolCache; -import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToFieldPostProcessorFactory; -import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToReturnPostProcessorFactory; +import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToPostProcessorFactory; import co.elastic.apm.agent.bci.methodmatching.MethodMatcher; import co.elastic.apm.agent.bci.methodmatching.TraceMethodInstrumentation; import co.elastic.apm.agent.configuration.CoreConfiguration; @@ -86,7 +84,6 @@ import static co.elastic.apm.agent.bci.bytebuddy.ClassLoaderNameMatcher.classLoaderWithName; import static co.elastic.apm.agent.bci.bytebuddy.ClassLoaderNameMatcher.isReflectionClassLoader; import static net.bytebuddy.asm.Advice.ExceptionHandler.Default.PRINTING; -import static net.bytebuddy.matcher.ElementMatchers.annotationType; import static net.bytebuddy.matcher.ElementMatchers.any; import static net.bytebuddy.matcher.ElementMatchers.is; import static net.bytebuddy.matcher.ElementMatchers.isInterface; @@ -302,10 +299,7 @@ public DynamicType.Builder transform(DynamicType.Builder builder, TypeDesc private static AgentBuilder.Transformer.ForAdvice getTransformer(final ElasticApmTracer tracer, final ElasticApmInstrumentation instrumentation, final Logger logger, final ElementMatcher methodMatcher) { Advice.WithCustomMapping withCustomMapping = Advice .withCustomMapping() - .with(new Advice.PostProcessor.Factory.Compound( - new AssignToArgumentPostProcessorFactory(), - new AssignToReturnPostProcessorFactory(), - new AssignToFieldPostProcessorFactory())) + .with(new AssignToPostProcessorFactory()) .bind(new SimpleMethodSignatureOffsetMappingFactory()) .bind(new AnnotationValueOffsetMappingFactory()); Advice.OffsetMapping.Factory offsetMapping = instrumentation.getOffsetMapping(); diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/ElasticApmInstrumentation.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/ElasticApmInstrumentation.java index 236a7e7a56..c4db3392a3 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/ElasticApmInstrumentation.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/ElasticApmInstrumentation.java @@ -80,6 +80,29 @@ public static AbstractSpan getActive() { return null; } + @Nullable + @VisibleForAdvice + public static Span getActiveSpan() { + if (tracer != null) { + final AbstractSpan active = tracer.getActive(); + if (active instanceof Span) { + return (Span) active; + } + } + return null; + } + + + @Nullable + @VisibleForAdvice + public static Span getActiveExitSpan() { + final Span span = getActiveSpan(); + if (span != null && span.isExit()) { + return span; + } + return null; + } + @Nullable @VisibleForAdvice public static Span createExitSpan() { diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignTo.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignTo.java new file mode 100644 index 0000000000..07c7e78796 --- /dev/null +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignTo.java @@ -0,0 +1,14 @@ +package co.elastic.apm.agent.bci.bytebuddy.postprocessor; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface AssignTo { + AssignToArgument[] arguments() default {}; + AssignToField[] fields() default {}; + AssignToReturn[] returns() default {}; +} diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToArgument.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToArgument.java index fe8f9c3650..c6b42ba9b5 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToArgument.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToArgument.java @@ -11,9 +11,9 @@ * the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -37,5 +37,7 @@ int value(); + int index() default -1; + Assigner.Typing typing() default Assigner.Typing.STATIC; } diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToArgumentPostProcessorFactory.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToArgumentPostProcessorFactory.java deleted file mode 100644 index 0bcecce540..0000000000 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToArgumentPostProcessorFactory.java +++ /dev/null @@ -1,64 +0,0 @@ -/*- - * #%L - * Elastic APM Java agent - * %% - * Copyright (C) 2018 - 2020 Elastic and contributors - * %% - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * #L% - */ -package co.elastic.apm.agent.bci.bytebuddy.postprocessor; - -import net.bytebuddy.asm.Advice; -import net.bytebuddy.description.annotation.AnnotationList; -import net.bytebuddy.description.method.MethodDescription; -import net.bytebuddy.description.method.ParameterDescription; -import net.bytebuddy.description.type.TypeDescription; -import net.bytebuddy.implementation.bytecode.StackManipulation; -import net.bytebuddy.implementation.bytecode.assign.Assigner; -import net.bytebuddy.implementation.bytecode.member.MethodVariableAccess; - -import static net.bytebuddy.matcher.ElementMatchers.annotationType; - -public class AssignToArgumentPostProcessorFactory implements Advice.PostProcessor.Factory { - @Override - public Advice.PostProcessor make(final MethodDescription.InDefinedShape adviceMethod, final boolean exit) { - final AnnotationList annotations = adviceMethod.getDeclaredAnnotations().filter(annotationType(AssignToArgument.class)); - if (!annotations.isEmpty()) { - final AssignToArgument assignTo = annotations.getOnly().prepare(AssignToArgument.class).load(); - return new Advice.PostProcessor() { - @Override - public StackManipulation resolve(TypeDescription instrumentedType, MethodDescription instrumentedMethod, Assigner assigner, Advice.ArgumentHandler argumentHandler) { - final ParameterDescription param = instrumentedMethod.getParameters().get(assignTo.value()); - final StackManipulation assign = assigner.assign(adviceMethod.getReturnType(), param.getType(), assignTo.typing()); - if (!assign.isValid()) { - throw new IllegalStateException("Cannot assign " + adviceMethod.getReturnType() + " to " + param.getType()); - } - return new StackManipulation.Compound( - MethodVariableAccess.of(adviceMethod.getReturnType()).loadFrom(exit ? argumentHandler.exit() : argumentHandler.enter()), - assign, - MethodVariableAccess.store(param) - ); - } - - }; - } else { - return Advice.PostProcessor.NoOp.INSTANCE; - } - } -} diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToField.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToField.java index 87e461511f..7cbb6e2958 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToField.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToField.java @@ -42,6 +42,8 @@ */ String value(); + int index() default -1; + /** * Returns the type that declares the field that should be mapped to the annotated parameter. If this property * is set to {@code void}, the field is looked up implicitly within the instrumented class's class hierarchy. @@ -52,20 +54,6 @@ */ Class declaringType() default Void.class; - /** - *

- * Indicates if it is possible to write to this parameter. If this property is set to {@code false}, the annotated - * type must be equal to the type declaring the instrumented method if the typing is not also set to {@link Assigner.Typing#DYNAMIC}. - * If this property is set to {@code true}, the annotated parameter can be any super type of the instrumented method's declaring type. - *

- *

- * Important: This property must be set to {@code true} if the advice method is not inlined. - *

- * - * @return {@code true} if this parameter is read-only. - */ - boolean readOnly() default true; - /** * The typing that should be applied when assigning the field value. * diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToPostProcessorFactory.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToPostProcessorFactory.java new file mode 100644 index 0000000000..7c25f2f166 --- /dev/null +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToPostProcessorFactory.java @@ -0,0 +1,232 @@ +/*- + * #%L + * Elastic APM Java agent + * %% + * Copyright (C) 2018 - 2020 Elastic and contributors + * %% + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * #L% + */ +package co.elastic.apm.agent.bci.bytebuddy.postprocessor; + +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.annotation.AnnotationDescription; +import net.bytebuddy.description.annotation.AnnotationList; +import net.bytebuddy.description.field.FieldDescription; +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.description.method.ParameterDescription; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.dynamic.TargetType; +import net.bytebuddy.dynamic.scaffold.FieldLocator; +import net.bytebuddy.implementation.bytecode.StackManipulation; +import net.bytebuddy.implementation.bytecode.assign.Assigner; +import net.bytebuddy.implementation.bytecode.collection.ArrayAccess; +import net.bytebuddy.implementation.bytecode.constant.IntegerConstant; +import net.bytebuddy.implementation.bytecode.member.FieldAccess; +import net.bytebuddy.implementation.bytecode.member.MethodVariableAccess; + +import java.util.ArrayList; +import java.util.List; + +import static net.bytebuddy.matcher.ElementMatchers.annotationType; + +public class AssignToPostProcessorFactory implements Advice.PostProcessor.Factory { + private static FieldLocator getFieldLocator(TypeDescription instrumentedType, AssignToField assignTo) { + if (assignTo.declaringType() == Void.class) { + return new FieldLocator.ForClassHierarchy(instrumentedType); + } else { + final TypeDescription declaringType = TypeDescription.ForLoadedType.of(assignTo.declaringType()); + if (!declaringType.represents(TargetType.class) && !instrumentedType.isAssignableTo(declaringType)) { + throw new IllegalStateException(declaringType + " is no super type of " + instrumentedType); + } + return new FieldLocator.ForExactType(declaringType); + } + } + + @Override + public Advice.PostProcessor make(final MethodDescription.InDefinedShape adviceMethod, final boolean exit) { + final AnnotationList annotations = adviceMethod.getDeclaredAnnotations() + .filter(annotationType(AssignToArgument.class) + .or(annotationType(AssignToField.class)) + .or(annotationType(AssignToReturn.class)) + .or(annotationType(AssignTo.class))); + if (annotations.isEmpty()) { + return Advice.PostProcessor.NoOp.INSTANCE; + } + final List postProcessors = new ArrayList<>(); + for (AnnotationDescription annotation : annotations) { + if (annotation.getAnnotationType().represents(AssignToArgument.class)) { + final AssignToArgument assignToArgument = annotations.getOnly().prepare(AssignToArgument.class).load(); + postProcessors.add(createAssignToArgumentPostProcessor(adviceMethod, exit, assignToArgument)); + } else if (annotation.getAnnotationType().represents(AssignToField.class)) { + final AssignToField assignToField = annotations.getOnly().prepare(AssignToField.class).load(); + postProcessors.add(createAssignToFieldPostProcessor(adviceMethod, exit, assignToField)); + } else if (annotation.getAnnotationType().represents(AssignToReturn.class)) { + final AssignToReturn assignToReturn = annotations.getOnly().prepare(AssignToReturn.class).load(); + postProcessors.add(createAssignToReturnPostProcessor(adviceMethod, exit, assignToReturn)); + } else if (annotation.getAnnotationType().represents(AssignTo.class)) { + final AssignTo assignTo = annotations.getOnly().prepare(AssignTo.class).load(); + for (AssignToArgument assignToArgument : assignTo.arguments()) { + postProcessors.add(createAssignToArgumentPostProcessor(adviceMethod, exit, assignToArgument)); + } + for (AssignToField assignToField : assignTo.fields()) { + postProcessors.add(createAssignToFieldPostProcessor(adviceMethod, exit, assignToField)); + } + for (AssignToReturn assignToReturn : assignTo.returns()) { + postProcessors.add(createAssignToReturnPostProcessor(adviceMethod, exit, assignToReturn)); + } + } + } + return new Compound(postProcessors); + } + + private Advice.PostProcessor createAssignToReturnPostProcessor(final MethodDescription.InDefinedShape adviceMethod, final boolean exit, final AssignToReturn assignToReturn) { + return new Advice.PostProcessor() { + @Override + public StackManipulation resolve(TypeDescription instrumentedType, MethodDescription instrumentedMethod, Assigner assigner, Advice.ArgumentHandler argumentHandler) { + final TypeDescription.Generic returnType = adviceMethod.getReturnType(); + if (assignToReturn.index() != -1) { + if (!returnType.represents(Object[].class)) { + throw new IllegalStateException("Advice method has to return Object[] when setting an index"); + } + return new StackManipulation.Compound( + MethodVariableAccess.REFERENCE.loadFrom(exit ? argumentHandler.exit() : argumentHandler.enter()), + IntegerConstant.forValue(assignToReturn.index()), + ArrayAccess.REFERENCE.load(), + assigner.assign(TypeDescription.Generic.OBJECT, instrumentedMethod.getReturnType(), Assigner.Typing.DYNAMIC), + MethodVariableAccess.of(instrumentedMethod.getReturnType()).storeAt(argumentHandler.returned()) + ); + } else { + final StackManipulation assign = assigner.assign(adviceMethod.getReturnType(), instrumentedMethod.getReturnType(), assignToReturn.typing()); + if (!assign.isValid()) { + throw new IllegalStateException("Cannot assign " + adviceMethod.getReturnType() + " to " + instrumentedMethod.getReturnType()); + } + return new StackManipulation.Compound( + MethodVariableAccess.of(adviceMethod.getReturnType()).loadFrom(exit ? argumentHandler.exit() : argumentHandler.enter()), + assign, + MethodVariableAccess.of(instrumentedMethod.getReturnType()).storeAt(argumentHandler.returned()) + ); + } + } + }; + } + + private Advice.PostProcessor createAssignToFieldPostProcessor(final MethodDescription.InDefinedShape adviceMethod, final boolean exit, final AssignToField assignToField) { + return new Advice.PostProcessor() { + @Override + public StackManipulation resolve(TypeDescription instrumentedType, MethodDescription instrumentedMethod, Assigner assigner, Advice.ArgumentHandler argumentHandler) { + final FieldDescription field = getFieldLocator(instrumentedType, assignToField).locate(assignToField.value()).getField(); + + if (!field.isStatic() && instrumentedMethod.isStatic()) { + throw new IllegalStateException("Cannot read non-static field " + field + " from static method " + instrumentedMethod); + } else if (instrumentedMethod.isConstructor() && !exit) { + throw new IllegalStateException("Cannot access non-static field before calling constructor: " + instrumentedMethod); + } + + final TypeDescription.Generic returnType = adviceMethod.getReturnType(); + if (assignToField.index() != -1) { + if (!returnType.represents(Object[].class)) { + throw new IllegalStateException("Advice method has to return Object[] when setting an index"); + } + return new StackManipulation.Compound( + MethodVariableAccess.loadThis(), + MethodVariableAccess.REFERENCE.loadFrom(exit ? argumentHandler.exit() : argumentHandler.enter()), + IntegerConstant.forValue(assignToField.index()), + ArrayAccess.REFERENCE.load(), + assigner.assign(TypeDescription.Generic.OBJECT, field.getType(), Assigner.Typing.DYNAMIC), + FieldAccess.forField(field).write() + ); + } else { + final StackManipulation assign = assigner.assign(adviceMethod.getReturnType(), field.getType(), assignToField.typing()); + if (!assign.isValid()) { + throw new IllegalStateException("Cannot assign " + adviceMethod.getReturnType() + " to " + field.getType()); + } + return new StackManipulation.Compound( + MethodVariableAccess.loadThis(), + MethodVariableAccess.of(adviceMethod.getReturnType()).loadFrom(exit ? argumentHandler.exit() : argumentHandler.enter()), + assign, + FieldAccess.forField(field).write() + ); + } + } + }; + } + + private Advice.PostProcessor createAssignToArgumentPostProcessor(final MethodDescription.InDefinedShape adviceMethod, final boolean exit, final AssignToArgument assignToArgument) { + return new Advice.PostProcessor() { + @Override + public StackManipulation resolve(TypeDescription instrumentedType, MethodDescription instrumentedMethod, Assigner assigner, Advice.ArgumentHandler argumentHandler) { + final ParameterDescription param = instrumentedMethod.getParameters().get(assignToArgument.value()); + final TypeDescription.Generic returnType = adviceMethod.getReturnType(); + if (assignToArgument.index() != -1) { + if (!returnType.represents(Object[].class)) { + throw new IllegalStateException("Advice method has to return Object[] when setting an index"); + } + return new StackManipulation.Compound( + MethodVariableAccess.REFERENCE.loadFrom(exit ? argumentHandler.exit() : argumentHandler.enter()), + IntegerConstant.forValue(assignToArgument.index()), + ArrayAccess.REFERENCE.load(), + assigner.assign(TypeDescription.Generic.OBJECT, param.getType(), Assigner.Typing.DYNAMIC), + MethodVariableAccess.store(param) + ); + } else { + final StackManipulation assign = assigner.assign(returnType, param.getType(), assignToArgument.typing()); + if (!assign.isValid()) { + throw new IllegalStateException("Cannot assign " + adviceMethod.getReturnType() + " to " + param.getType()); + } + return new StackManipulation.Compound( + MethodVariableAccess.of(adviceMethod.getReturnType()).loadFrom(exit ? argumentHandler.exit() : argumentHandler.enter()), + assign, + MethodVariableAccess.store(param) + ); + } + } + }; + } + + public static class Compound implements Advice.PostProcessor { + + /** + * The represented post processors. + */ + private final List postProcessors; + + /** + * Creates a new compound post processor. + * + * @param postProcessors The represented post processors. + */ + public Compound(List postProcessors) { + this.postProcessors = postProcessors; + } + + /** + * {@inheritDoc} + */ + public StackManipulation resolve(TypeDescription instrumentedType, + MethodDescription instrumentedMethod, + Assigner assigner, + Advice.ArgumentHandler argumentHandler) { + List stackManipulations = new ArrayList(postProcessors.size()); + for (Advice.PostProcessor postProcessor : postProcessors) { + stackManipulations.add(postProcessor.resolve(instrumentedType, instrumentedMethod, assigner, argumentHandler)); + } + return new StackManipulation.Compound(stackManipulations); + } + } +} diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToReturn.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToReturn.java index 9114ed4d81..42c26f3e3b 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToReturn.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToReturn.java @@ -11,9 +11,9 @@ * the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -36,4 +36,6 @@ public @interface AssignToReturn { Assigner.Typing typing() default Assigner.Typing.STATIC; + + int index() default -1; } diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/util/RemoveOnGetThreadLocal.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/util/RemoveOnGetThreadLocal.java new file mode 100644 index 0000000000..8cff409864 --- /dev/null +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/util/RemoveOnGetThreadLocal.java @@ -0,0 +1,22 @@ +package co.elastic.apm.agent.util; + +import javax.annotation.Nullable; + +public class RemoveOnGetThreadLocal { + + private final ThreadLocal threadLocal = new ThreadLocal<>(); + + public void set(@Nullable T value) { + threadLocal.set(value); + } + + @Nullable + public T getAndRemove() { + T value = threadLocal.get(); + if (value != null) { + threadLocal.remove(); + } + return value; + } + +} diff --git a/apm-agent-core/src/test/java/co/elastic/apm/agent/bci/InstrumentationTest.java b/apm-agent-core/src/test/java/co/elastic/apm/agent/bci/InstrumentationTest.java index c92a7f32c9..86832d460f 100644 --- a/apm-agent-core/src/test/java/co/elastic/apm/agent/bci/InstrumentationTest.java +++ b/apm-agent-core/src/test/java/co/elastic/apm/agent/bci/InstrumentationTest.java @@ -25,6 +25,7 @@ package co.elastic.apm.agent.bci; import co.elastic.apm.agent.MockTracer; +import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignTo; import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToArgument; import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToField; import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToReturn; @@ -46,6 +47,7 @@ import org.junit.jupiter.api.Test; import org.stagemonitor.configuration.ConfigurationRegistry; +import javax.annotation.Nullable; import java.util.Collection; import java.util.Collections; import java.util.List; @@ -86,6 +88,13 @@ void testFieldAccess() { assertThat(privateString).isEqualTo("@AssignToField"); } + @Test + void testFieldAccessArray() { + init(configurationRegistry, List.of(new FieldAccessArrayInstrumentation())); + assignToField("@AssignToField"); + assertThat(privateString).isEqualTo("@AssignToField"); + } + public void assignToField(String s) { } @@ -99,6 +108,27 @@ public String assignToArgument(String s) { return s; } + @Test + void testAssignToArgumentArray() { + init(configurationRegistry, List.of(new AssignToArgumentsInstrumentation())); + assertThat(assignToArguments("foo", "bar")).isEqualTo("barfoo"); + } + + public String assignToArguments(String foo, String bar) { + return foo + bar; + } + + @Test + void testAssignToReturnArray() { + init(configurationRegistry, List.of(new AssignToReturnArrayInstrumentation())); + assertThat(assignToReturn("foo", "bar")).isEqualTo("foobar"); + } + + @Nullable + public String assignToReturn(String foo, String bar) { + return null; + } + @Test void testDisabled() { when(coreConfig.getDisabledInstrumentations()). @@ -358,6 +388,31 @@ public static String onEnter(@Advice.Argument(0) String s) { public ElementMatcher getTypeMatcher() { return ElementMatchers.named("co.elastic.apm.agent.bci.InstrumentationTest"); } + + @Override + public ElementMatcher getMethodMatcher() { + return ElementMatchers.named("assignToField"); + } + + @Override + public Collection getInstrumentationGroupNames() { + return List.of("test", "experimental"); + } + } + + public static class FieldAccessArrayInstrumentation extends ElasticApmInstrumentation { + + @AssignTo(fields = @AssignToField(index = 0, value = "privateString")) + @Advice.OnMethodEnter + public static Object[] onEnter(@Advice.Argument(0) String s) { + return new Object[]{s}; + } + + @Override + public ElementMatcher getTypeMatcher() { + return ElementMatchers.named("co.elastic.apm.agent.bci.InstrumentationTest"); + } + @Override public ElementMatcher getMethodMatcher() { return ElementMatchers.named("assignToField"); @@ -381,6 +436,7 @@ public static String onEnter(@Advice.Argument(0) String s) { public ElementMatcher getTypeMatcher() { return ElementMatchers.named("co.elastic.apm.agent.bci.InstrumentationTest"); } + @Override public ElementMatcher getMethodMatcher() { return ElementMatchers.named("assignToArgument"); @@ -391,4 +447,55 @@ public Collection getInstrumentationGroupNames() { return List.of("test", "experimental"); } } + + public static class AssignToArgumentsInstrumentation extends ElasticApmInstrumentation { + + @AssignTo(arguments = { + @AssignToArgument(index = 0, value = 1), + @AssignToArgument(index = 1, value = 0) + }) + @Advice.OnMethodEnter + public static Object[] onEnter(@Advice.Argument(0) String foo, @Advice.Argument(1) String bar) { + return new Object[]{foo, bar}; + } + + @Override + public ElementMatcher getTypeMatcher() { + return ElementMatchers.named("co.elastic.apm.agent.bci.InstrumentationTest"); + } + + @Override + public ElementMatcher getMethodMatcher() { + return ElementMatchers.named("assignToArguments"); + } + + @Override + public Collection getInstrumentationGroupNames() { + return List.of("test", "experimental"); + } + } + + public static class AssignToReturnArrayInstrumentation extends ElasticApmInstrumentation { + + @AssignTo(returns = @AssignToReturn(index = 0)) + @Advice.OnMethodExit + public static Object[] onEnter(@Advice.Argument(0) String foo, @Advice.Argument(1) String bar) { + return new Object[]{foo + bar}; + } + + @Override + public ElementMatcher getTypeMatcher() { + return ElementMatchers.named("co.elastic.apm.agent.bci.InstrumentationTest"); + } + + @Override + public ElementMatcher getMethodMatcher() { + return ElementMatchers.named("assignToReturn"); + } + + @Override + public Collection getInstrumentationGroupNames() { + return List.of("test", "experimental"); + } + } } diff --git a/apm-agent-plugins/apm-apache-httpclient-plugin/src/main/java/co/elastic/apm/agent/httpclient/ApacheHttpAsyncClientInstrumentation.java b/apm-agent-plugins/apm-apache-httpclient-plugin/src/main/java/co/elastic/apm/agent/httpclient/ApacheHttpAsyncClientInstrumentation.java index 420b6ecaf8..07e0a606bd 100644 --- a/apm-agent-plugins/apm-apache-httpclient-plugin/src/main/java/co/elastic/apm/agent/httpclient/ApacheHttpAsyncClientInstrumentation.java +++ b/apm-agent-plugins/apm-apache-httpclient-plugin/src/main/java/co/elastic/apm/agent/httpclient/ApacheHttpAsyncClientInstrumentation.java @@ -26,6 +26,8 @@ import co.elastic.apm.agent.bci.HelperClassManager; import co.elastic.apm.agent.bci.VisibleForAdvice; +import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignTo; +import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToArgument; import co.elastic.apm.agent.http.client.HttpClientHelper; import co.elastic.apm.agent.httpclient.helper.ApacheHttpAsyncClientHelper; import co.elastic.apm.agent.impl.ElasticApmTracer; @@ -59,51 +61,6 @@ public class ApacheHttpAsyncClientInstrumentation extends BaseApacheHttpClientIn @VisibleForAdvice public static HelperClassManager, HttpContext, HttpRequest>> asyncHelperManager; - private static class ApacheHttpAsyncClientAdvice { - @Advice.OnMethodEnter(suppress = Throwable.class) - private static void onBeforeExecute(@Advice.Argument(value = 0, readOnly = false) HttpAsyncRequestProducer requestProducer, - @Advice.Argument(2) HttpContext context, - @Advice.Argument(value = 3, readOnly = false) FutureCallback futureCallback, - @Advice.Local("span") @Nullable Span span, - @Advice.Local("wrapped") boolean wrapped) { - if (tracer == null || tracer.getActive() == null) { - return; - } - final AbstractSpan parent = tracer.getActive(); - span = parent.createExitSpan(); - if (span != null) { - span.withType(HttpClientHelper.EXTERNAL_TYPE) - .withSubtype(HttpClientHelper.HTTP_SUBTYPE) - .activate(); - - ApacheHttpAsyncClientHelper, HttpContext, HttpRequest> asyncHelper = - asyncHelperManager.getForClassLoaderOfClass(HttpAsyncRequestProducer.class); - TextHeaderSetter headerSetter = headerSetterHelperClassManager.getForClassLoaderOfClass(HttpRequest.class); - if (asyncHelper != null && headerSetter != null) { - requestProducer = asyncHelper.wrapRequestProducer(requestProducer, span, headerSetter); - futureCallback = asyncHelper.wrapFutureCallback(futureCallback, context, span); - wrapped = true; - } - } - } - - @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) - public static void onAfterExecute(@Advice.Local("span") @Nullable Span span, - @Advice.Local("wrapped") boolean wrapped, - @Advice.Thrown @Nullable Throwable t) { - if (span != null) { - // Deactivate in this thread. Span will be ended and reported by the listener - span.deactivate(); - - if (!wrapped) { - // Listener is not wrapped- we need to end the span so to avoid leak and report error if occurred during method invocation - span.captureException(t); - span.end(); - } - } - } - } - public ApacheHttpAsyncClientInstrumentation(ElasticApmTracer tracer) { super(tracer); asyncHelperManager = HelperClassManager.ForAnyClassLoader.of(tracer, @@ -144,4 +101,53 @@ public ElementMatcher getMethodMatcher() { .and(takesArgument(2, named("org.apache.http.protocol.HttpContext"))) .and(takesArgument(3, named("org.apache.http.concurrent.FutureCallback"))); } + + public static class ApacheHttpAsyncClientAdvice { + @AssignTo(arguments = { + @AssignToArgument(index = 0, value = 0), + @AssignToArgument(index = 1, value = 3) + }) + @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) + public static Object[] onBeforeExecute(@Advice.Argument(value = 0) HttpAsyncRequestProducer requestProducer, + @Advice.Argument(2) HttpContext context, + @Advice.Argument(value = 3) FutureCallback futureCallback) { + if (tracer == null || tracer.getActive() == null) { + return new Object[]{requestProducer, futureCallback, false, null}; + } + final AbstractSpan parent = tracer.getActive(); + Span span = parent.createExitSpan(); + boolean wrapped = false; + if (span != null) { + span.withType(HttpClientHelper.EXTERNAL_TYPE) + .withSubtype(HttpClientHelper.HTTP_SUBTYPE) + .activate(); + + ApacheHttpAsyncClientHelper, HttpContext, HttpRequest> asyncHelper = + asyncHelperManager.getForClassLoaderOfClass(HttpAsyncRequestProducer.class); + TextHeaderSetter headerSetter = headerSetterHelperClassManager.getForClassLoaderOfClass(HttpRequest.class); + if (asyncHelper != null && headerSetter != null) { + requestProducer = asyncHelper.wrapRequestProducer(requestProducer, span, headerSetter); + futureCallback = asyncHelper.wrapFutureCallback(futureCallback, context, span); + wrapped = true; + } + } + return new Object[]{requestProducer, futureCallback, wrapped, span}; + } + + @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class, inline = false) + public static void onAfterExecute(@Advice.Enter @Nullable Object[] enter, + @Advice.Thrown @Nullable Throwable t) { + Span span = enter != null ? (Span) enter[3] : null; + if (span != null) { + // Deactivate in this thread. Span will be ended and reported by the listener + span.deactivate(); + + if (!((Boolean) enter[2])) { + // Listener is not wrapped- we need to end the span so to avoid leak and report error if occurred during method invocation + span.captureException(t); + span.end(); + } + } + } + } } diff --git a/apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-5_6/src/main/java/co/elastic/apm/agent/es/restclient/v5_6/ElasticsearchClientAsyncInstrumentation.java b/apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-5_6/src/main/java/co/elastic/apm/agent/es/restclient/v5_6/ElasticsearchClientAsyncInstrumentation.java index ebaddefc6b..20c106a7bb 100644 --- a/apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-5_6/src/main/java/co/elastic/apm/agent/es/restclient/v5_6/ElasticsearchClientAsyncInstrumentation.java +++ b/apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-5_6/src/main/java/co/elastic/apm/agent/es/restclient/v5_6/ElasticsearchClientAsyncInstrumentation.java @@ -11,9 +11,9 @@ * the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -24,10 +24,12 @@ */ package co.elastic.apm.agent.es.restclient.v5_6; +import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToArgument; import co.elastic.apm.agent.es.restclient.ElasticsearchRestClientInstrumentation; import co.elastic.apm.agent.es.restclient.ElasticsearchRestClientInstrumentationHelper; import co.elastic.apm.agent.impl.ElasticApmTracer; import co.elastic.apm.agent.impl.transaction.Span; +import co.elastic.apm.agent.util.RemoveOnGetThreadLocal; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.method.MethodDescription; import net.bytebuddy.description.type.TypeDescription; @@ -73,40 +75,32 @@ public ElementMatcher getMethodMatcher() { } - private static class ElasticsearchRestClientAsyncAdvice { - @Advice.OnMethodEnter(suppress = Throwable.class) - private static void onBeforeExecute(@Advice.Argument(0) String method, - @Advice.Argument(1) String endpoint, - @Advice.Argument(3) @Nullable HttpEntity entity, - @Advice.Argument(value = 5, readOnly = false) ResponseListener responseListener, - @Advice.Local("span") Span span, - @Advice.Local("wrapped") boolean wrapped, - @Advice.Local("helper") ElasticsearchRestClientInstrumentationHelper helper) { + public static class ElasticsearchRestClientAsyncAdvice { + private static final RemoveOnGetThreadLocal spanTls = new RemoveOnGetThreadLocal<>(); + @AssignToArgument(5) + @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) + public static ResponseListener onBeforeExecute(@Advice.Argument(0) String method, + @Advice.Argument(1) String endpoint, + @Advice.Argument(3) @Nullable HttpEntity entity, + @Advice.Argument(5) ResponseListener responseListener) { - helper = esClientInstrHelperManager.getForClassLoaderOfClass(Response.class); + ElasticsearchRestClientInstrumentationHelper helper = esClientInstrHelperManager.getForClassLoaderOfClass(Response.class); if (helper != null) { - span = helper.createClientSpan(method, endpoint, entity); + Span span = helper.createClientSpan(method, endpoint, entity); + spanTls.set(span); if (span != null) { - responseListener = helper.wrapResponseListener(responseListener, span); - wrapped = true; + return helper.wrapResponseListener(responseListener, span); } } + return responseListener; } - @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) - private static void onAfterExecute(@Advice.Argument(5) ResponseListener responseListener, - @Advice.Local("span") @Nullable Span span, - @Advice.Local("wrapped") boolean wrapped, - @Advice.Local("helper") @Nullable ElasticsearchRestClientInstrumentationHelper helper, - @Advice.Thrown @Nullable Throwable t) { + @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class, inline = false) + public static void onAfterExecute(@Advice.Thrown @Nullable Throwable t) { + final Span span = spanTls.getAndRemove(); if (span != null) { // Deactivate in this thread. Span will be ended and reported by the listener span.deactivate(); - - if (!wrapped) { - // Listener is not wrapped- we need to end the span so to avoid leak and report error if occurred during method invocation - helper.finishClientSpan(null, span, t); - } } } } diff --git a/apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-5_6/src/main/java/co/elastic/apm/agent/es/restclient/v5_6/ElasticsearchClientSyncInstrumentation.java b/apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-5_6/src/main/java/co/elastic/apm/agent/es/restclient/v5_6/ElasticsearchClientSyncInstrumentation.java index f644d57228..231ed3ead2 100644 --- a/apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-5_6/src/main/java/co/elastic/apm/agent/es/restclient/v5_6/ElasticsearchClientSyncInstrumentation.java +++ b/apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-5_6/src/main/java/co/elastic/apm/agent/es/restclient/v5_6/ElasticsearchClientSyncInstrumentation.java @@ -11,9 +11,9 @@ * the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -34,6 +34,7 @@ import net.bytebuddy.matcher.ElementMatcher; import org.apache.http.HttpEntity; import org.elasticsearch.client.Response; +import org.elasticsearch.client.ResponseListener; import javax.annotation.Nullable; @@ -49,24 +50,26 @@ public ElasticsearchClientSyncInstrumentation(ElasticApmTracer tracer) { super(tracer); } - private static class ElasticsearchRestClientAdvice { - @Advice.OnMethodEnter(suppress = Throwable.class) - private static void onBeforeExecute(@Advice.Argument(0) String method, + public static class ElasticsearchRestClientAdvice { + @Nullable + @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) + public static Span onBeforeExecute(@Advice.Argument(0) String method, @Advice.Argument(1) String endpoint, - @Advice.Argument(3) @Nullable HttpEntity entity, - @Advice.Local("span") Span span, - @Advice.Local("helper") ElasticsearchRestClientInstrumentationHelper helper) { - helper = esClientInstrHelperManager.getForClassLoaderOfClass(Response.class); - if (helper != null) { - span = helper.createClientSpan(method, endpoint, entity); + @Advice.Argument(3) @Nullable HttpEntity entity) { + ElasticsearchRestClientInstrumentationHelper helper = + esClientInstrHelperManager.getForClassLoaderOfClass(Response.class); + if (helper == null) { + return null; } + return helper.createClientSpan(method, endpoint, entity); } - @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) + @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class, inline = false) public static void onAfterExecute(@Advice.Return @Nullable Response response, - @Advice.Local("span") @Nullable Span span, - @Advice.Local("helper") @Nullable ElasticsearchRestClientInstrumentationHelper helper, + @Advice.Enter @Nullable Span span, @Advice.Thrown @Nullable Throwable t) { + ElasticsearchRestClientInstrumentationHelper helper = + esClientInstrHelperManager.getForClassLoaderOfClass(Response.class); if (helper != null && span != null) { try { helper.finishClientSpan(response, span, t); diff --git a/apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-6_4/src/main/java/co/elastic/apm/agent/es/restclient/v6_4/ElasticsearchClientAsyncInstrumentation.java b/apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-6_4/src/main/java/co/elastic/apm/agent/es/restclient/v6_4/ElasticsearchClientAsyncInstrumentation.java index 6ab505f800..916ef9f1d7 100644 --- a/apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-6_4/src/main/java/co/elastic/apm/agent/es/restclient/v6_4/ElasticsearchClientAsyncInstrumentation.java +++ b/apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-6_4/src/main/java/co/elastic/apm/agent/es/restclient/v6_4/ElasticsearchClientAsyncInstrumentation.java @@ -11,9 +11,9 @@ * the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -24,10 +24,12 @@ */ package co.elastic.apm.agent.es.restclient.v6_4; +import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToArgument; import co.elastic.apm.agent.es.restclient.ElasticsearchRestClientInstrumentation; import co.elastic.apm.agent.es.restclient.ElasticsearchRestClientInstrumentationHelper; import co.elastic.apm.agent.impl.ElasticApmTracer; import co.elastic.apm.agent.impl.transaction.Span; +import co.elastic.apm.agent.util.RemoveOnGetThreadLocal; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.method.MethodDescription; import net.bytebuddy.description.type.TypeDescription; @@ -67,38 +69,30 @@ public ElementMatcher getMethodMatcher() { .and(takesArgument(1, named("org.elasticsearch.client.ResponseListener")))); } - private static class ElasticsearchRestClientAsyncAdvice { - @Advice.OnMethodEnter(suppress = Throwable.class) - private static void onBeforeExecute(@Advice.Argument(0) Request request, - @Advice.Argument(value = 1, readOnly = false) ResponseListener responseListener, - @Advice.Local("span") Span span, - @Advice.Local("wrapped") boolean wrapped, - @Advice.Local("helper") ElasticsearchRestClientInstrumentationHelper helper) { + public static class ElasticsearchRestClientAsyncAdvice { + private static final RemoveOnGetThreadLocal spanTls = new RemoveOnGetThreadLocal<>(); - helper = esClientInstrHelperManager.getForClassLoaderOfClass(Request.class); + @AssignToArgument(1) + @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) + public static ResponseListener onBeforeExecute(@Advice.Argument(0) Request request, + @Advice.Argument(1) ResponseListener responseListener) { + ElasticsearchRestClientInstrumentationHelper helper = esClientInstrHelperManager.getForClassLoaderOfClass(Request.class); if (helper != null) { - span = helper.createClientSpan(request.getMethod(), request.getEndpoint(), request.getEntity()); + Span span = helper.createClientSpan(request.getMethod(), request.getEndpoint(), request.getEntity()); if (span != null) { - responseListener = helper.wrapResponseListener(responseListener, span); - wrapped = true; + spanTls.set(span); + return helper.wrapResponseListener(responseListener, span); } } + return responseListener; } - @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) - private static void onAfterExecute(@Advice.Argument(1) ResponseListener responseListener, - @Advice.Local("span") @Nullable Span span, - @Advice.Local("wrapped") boolean wrapped, - @Advice.Local("helper") @Nullable ElasticsearchRestClientInstrumentationHelper helper, - @Advice.Thrown @Nullable Throwable t) { + @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class, inline = false) + public static void onAfterExecute(@Advice.Thrown @Nullable Throwable t) { + final Span span = spanTls.getAndRemove(); if (span != null) { // Deactivate in this thread. Span will be ended and reported by the listener span.deactivate(); - - if (!wrapped) { - // Listener is not wrapped- we need to end the span so to avoid leak and report error if occurred during method invocation - helper.finishClientSpan(null, span, t); - } } } } diff --git a/apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-6_4/src/main/java/co/elastic/apm/agent/es/restclient/v6_4/ElasticsearchClientSyncInstrumentation.java b/apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-6_4/src/main/java/co/elastic/apm/agent/es/restclient/v6_4/ElasticsearchClientSyncInstrumentation.java index c654202f8f..9690336839 100644 --- a/apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-6_4/src/main/java/co/elastic/apm/agent/es/restclient/v6_4/ElasticsearchClientSyncInstrumentation.java +++ b/apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-6_4/src/main/java/co/elastic/apm/agent/es/restclient/v6_4/ElasticsearchClientSyncInstrumentation.java @@ -11,9 +11,9 @@ * the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -66,23 +66,25 @@ public ElementMatcher getMethodMatcher() { .and(takesArgument(0, named("org.elasticsearch.client.Request")))); } - private static class ElasticsearchRestClientSyncAdvice { - @Advice.OnMethodEnter(suppress = Throwable.class) - private static void onBeforeExecute(@Advice.Argument(0) Request request, - @Advice.Local("span") Span span, - @Advice.Local("helper") ElasticsearchRestClientInstrumentationHelper helper) { + public static class ElasticsearchRestClientSyncAdvice { + @Nullable + @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) + public static Span onBeforeExecute(@Advice.Argument(0) Request request) { - helper = esClientInstrHelperManager.getForClassLoaderOfClass(Request.class); - if (helper != null) { - span = helper.createClientSpan(request.getMethod(), request.getEndpoint(), request.getEntity()); + ElasticsearchRestClientInstrumentationHelper helper = + esClientInstrHelperManager.getForClassLoaderOfClass(Request.class); + if (helper == null) { + return null; } + return helper.createClientSpan(request.getMethod(), request.getEndpoint(), request.getEntity()); } - @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) + @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class, inline = false) public static void onAfterExecute(@Advice.Return @Nullable Response response, - @Advice.Local("span") @Nullable Span span, - @Advice.Local("helper") @Nullable ElasticsearchRestClientInstrumentationHelper helper, + @Advice.Enter @Nullable Span span, @Advice.Thrown @Nullable Throwable t) { + ElasticsearchRestClientInstrumentationHelper helper = + esClientInstrHelperManager.getForClassLoaderOfClass(Request.class); if (helper != null && span != null) { try { helper.finishClientSpan(response, span, t); diff --git a/apm-agent-plugins/apm-kafka-plugin/apm-kafka-base-plugin/src/main/java/co/elastic/apm/agent/kafka/KafkaProducerInstrumentation.java b/apm-agent-plugins/apm-kafka-plugin/apm-kafka-base-plugin/src/main/java/co/elastic/apm/agent/kafka/KafkaProducerInstrumentation.java index c8c7444acb..d71f2d460a 100644 --- a/apm-agent-plugins/apm-kafka-plugin/apm-kafka-base-plugin/src/main/java/co/elastic/apm/agent/kafka/KafkaProducerInstrumentation.java +++ b/apm-agent-plugins/apm-kafka-plugin/apm-kafka-base-plugin/src/main/java/co/elastic/apm/agent/kafka/KafkaProducerInstrumentation.java @@ -11,9 +11,9 @@ * the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -24,6 +24,7 @@ */ package co.elastic.apm.agent.kafka; +import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToArgument; import co.elastic.apm.agent.impl.ElasticApmTracer; import co.elastic.apm.agent.impl.transaction.Span; import co.elastic.apm.agent.kafka.helper.KafkaInstrumentationHelper; @@ -73,40 +74,36 @@ public Class getAdviceClass() { @SuppressWarnings("rawtypes") public static class KafkaProducerAdvice { - @SuppressWarnings({"unused", "DuplicatedCode", "ParameterCanBeLocal"}) - @Advice.OnMethodEnter(suppress = Throwable.class) @Nullable - public static Span beforeSend(@Advice.Argument(0) final ProducerRecord record, - @Advice.Argument(value = 1, readOnly = false) @Nullable Callback callback, - @Advice.Local("helper") @Nullable KafkaInstrumentationHelper helper) { + @AssignToArgument(1) + @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) + public static Callback beforeSend(@Advice.Argument(0) final ProducerRecord record, + @Advice.Argument(1) @Nullable Callback callback) { if (tracer == null) { - return null; + return callback; } Span span = null; //noinspection ConstantConditions - helper = kafkaInstrHelperManager.getForClassLoaderOfClass(KafkaProducer.class); + KafkaInstrumentationHelper helper = kafkaInstrHelperManager.getForClassLoaderOfClass(KafkaProducer.class); if (helper != null) { span = helper.onSendStart(record); } if (span == null) { - return null; + return callback; } - //noinspection UnusedAssignment - callback = helper.wrapCallback(callback, span); - return span; + return helper.wrapCallback(callback, span); } - @SuppressWarnings("unused") - @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) - public static void afterSend(@Advice.Enter @Nullable final Span span, - @Advice.Argument(0) final ProducerRecord record, + @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class, inline = false) + public static void afterSend(@Advice.Argument(0) final ProducerRecord record, @Advice.This final KafkaProducer thiz, - @Advice.Local("helper") @Nullable KafkaInstrumentationHelper helper, @Advice.Thrown final Throwable throwable) { - + final Span span = getActiveExitSpan(); + //noinspection ConstantConditions + KafkaInstrumentationHelper helper = kafkaInstrHelperManager.getForClassLoaderOfClass(KafkaProducer.class); if (helper != null && span != null) { helper.onSendEnd(span, record, thiz, throwable); } diff --git a/apm-agent-plugins/apm-kafka-plugin/apm-kafka-headers-plugin/src/main/java/co/elastic/apm/agent/kafka/KafkaProducerHeadersInstrumentation.java b/apm-agent-plugins/apm-kafka-plugin/apm-kafka-headers-plugin/src/main/java/co/elastic/apm/agent/kafka/KafkaProducerHeadersInstrumentation.java index 1b51d3ff67..f73bf8c311 100644 --- a/apm-agent-plugins/apm-kafka-plugin/apm-kafka-headers-plugin/src/main/java/co/elastic/apm/agent/kafka/KafkaProducerHeadersInstrumentation.java +++ b/apm-agent-plugins/apm-kafka-plugin/apm-kafka-headers-plugin/src/main/java/co/elastic/apm/agent/kafka/KafkaProducerHeadersInstrumentation.java @@ -83,7 +83,6 @@ public static class KafkaProducerHeadersAdvice { @Nullable public static Span beforeSend(@Advice.FieldValue("apiVersions") final ApiVersions apiVersions, @Advice.Argument(0) final ProducerRecord record, - @Advice.Local("helper") @Nullable KafkaInstrumentationHelper helper, @Nullable @Advice.Argument(value = 1, readOnly = false) Callback callback) { if (tracer == null) { return null; @@ -91,7 +90,7 @@ public static Span beforeSend(@Advice.FieldValue("apiVersions") final ApiVersion Span span = null; //noinspection ConstantConditions - helper = kafkaInstrHelperManager.getForClassLoaderOfClass(KafkaProducer.class); + KafkaInstrumentationHelper helper = kafkaInstrHelperManager.getForClassLoaderOfClass(KafkaProducer.class); if (helper != null) { span = helper.onSendStart(record); @@ -126,7 +125,6 @@ public static Span beforeSend(@Advice.FieldValue("apiVersions") final ApiVersion public static boolean afterSend(@Advice.Enter(readOnly = false) @Nullable Span span, @Advice.Argument(value = 0, readOnly = false) ProducerRecord record, @Advice.This final KafkaProducer thiz, - @Advice.Local("helper") @Nullable KafkaInstrumentationHelper helper, @Advice.Thrown @Nullable final Throwable throwable) { if (throwable != null && throwable.getMessage().contains("Magic v1 does not support record headers")) { @@ -149,6 +147,8 @@ record = new ProducerRecord(record.topic(), record.partition(), record.timestamp return true; } } + //noinspection ConstantConditions + KafkaInstrumentationHelper helper = kafkaInstrHelperManager.getForClassLoaderOfClass(KafkaProducer.class); if (helper != null && span != null) { helper.onSendEnd(span, record, thiz, throwable); } diff --git a/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttp3ClientAsyncInstrumentation.java b/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttp3ClientAsyncInstrumentation.java index 6bc245fc3b..a56cbb0470 100644 --- a/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttp3ClientAsyncInstrumentation.java +++ b/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttp3ClientAsyncInstrumentation.java @@ -26,6 +26,9 @@ import co.elastic.apm.agent.bci.HelperClassManager; import co.elastic.apm.agent.bci.VisibleForAdvice; +import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignTo; +import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToArgument; +import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToField; import co.elastic.apm.agent.http.client.HttpClientHelper; import co.elastic.apm.agent.impl.ElasticApmTracer; import co.elastic.apm.agent.impl.transaction.AbstractSpan; @@ -34,7 +37,6 @@ import net.bytebuddy.asm.Advice; import net.bytebuddy.description.method.MethodDescription; import net.bytebuddy.description.type.TypeDescription; -import net.bytebuddy.implementation.bytecode.assign.Assigner; import net.bytebuddy.matcher.ElementMatcher; import okhttp3.Call; import okhttp3.Callback; @@ -80,25 +82,28 @@ public OkHttp3ClientAsyncInstrumentation(ElasticApmTracer tracer) { @VisibleForAdvice public static class OkHttpClient3ExecuteAdvice { - @Advice.OnMethodEnter(suppress = Throwable.class) - private static void onBeforeEnqueue(@Advice.Origin Class clazz, - @Advice.FieldValue(value = "originalRequest", typing = Assigner.Typing.DYNAMIC, readOnly = false) @Nullable okhttp3.Request originalRequest, - @Advice.Argument(value = 0, readOnly = false) @Nullable Callback callback, - @Advice.Local("span") Span span) { + @AssignTo( + fields = @AssignToField(index = 0, value = "originalRequest"), + arguments = @AssignToArgument(index = 1, value = 0) + ) + @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) + public static Object[] onBeforeEnqueue(@Advice.Origin Class clazz, + @Advice.FieldValue("originalRequest") @Nullable okhttp3.Request originalRequest, + @Advice.Argument(0) @Nullable Callback callback) { if (tracer == null || tracer.getActive() == null || callbackWrapperCreator == null) { - return; + return new Object[] {originalRequest, callback, null}; } final WrapperCreator wrapperCreator = callbackWrapperCreator.getForClassLoaderOfClass(clazz); if (originalRequest == null || callback == null || wrapperCreator == null) { - return; + return new Object[] {originalRequest, callback, null}; } final AbstractSpan parent = tracer.getActive(); okhttp3.Request request = originalRequest; HttpUrl url = request.url(); - span = HttpClientHelper.startHttpClientSpan(parent, request.method(), url.toString(), url.scheme(), + Span span = HttpClientHelper.startHttpClientSpan(parent, request.method(), url.toString(), url.scheme(), OkHttpClientHelper.computeHostName(url.host()), url.port()); if (span != null) { span.activate(); @@ -112,10 +117,12 @@ private static void onBeforeEnqueue(@Advice.Origin Class clazz, } callback = wrapperCreator.wrap(callback, span); } + return new Object[] {originalRequest, callback, span}; } - @Advice.OnMethodExit(suppress = Throwable.class) - private static void onAfterEnqueue(@Advice.Local("span") @Nullable Span span) { + @Advice.OnMethodExit(suppress = Throwable.class, inline = false) + public static void onAfterEnqueue(@Advice.Enter @Nullable Object[] enter) { + Span span = enter != null ? (Span) enter[2] : null; if (span != null) { span.deactivate(); } diff --git a/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttp3ClientInstrumentation.java b/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttp3ClientInstrumentation.java index dd7a78523f..fc624caec1 100644 --- a/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttp3ClientInstrumentation.java +++ b/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttp3ClientInstrumentation.java @@ -25,11 +25,13 @@ package co.elastic.apm.agent.okhttp; import co.elastic.apm.agent.bci.VisibleForAdvice; +import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToField; import co.elastic.apm.agent.http.client.HttpClientHelper; import co.elastic.apm.agent.impl.ElasticApmTracer; import co.elastic.apm.agent.impl.transaction.AbstractSpan; import co.elastic.apm.agent.impl.transaction.Span; import co.elastic.apm.agent.impl.transaction.TextHeaderSetter; +import co.elastic.apm.agent.util.RemoveOnGetThreadLocal; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.method.MethodDescription; import net.bytebuddy.description.type.TypeDescription; @@ -57,52 +59,51 @@ public Class getAdviceClass() { @VisibleForAdvice public static class OkHttpClient3ExecuteAdvice { - @Advice.OnMethodEnter(suppress = Throwable.class) - private static void onBeforeExecute( @Advice.FieldValue(value = "originalRequest", typing = Assigner.Typing.DYNAMIC, readOnly = false) @Nullable Object originalRequest, - @Advice.Local("span") Span span) { + private final static RemoveOnGetThreadLocal spanTls = new RemoveOnGetThreadLocal<>(); - if (tracer == null || tracer.getActive() == null) { - return; - } + @Nullable + @AssignToField(value = "originalRequest", typing = Assigner.Typing.DYNAMIC) + @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) + public static Object onBeforeExecute(@Advice.FieldValue("originalRequest") @Nullable Object originalRequest) { - if (originalRequest == null) { - return; + if (tracer == null || tracer.getActive() == null || !(originalRequest instanceof Request)) { + return originalRequest; } final AbstractSpan parent = tracer.getActive(); - if (originalRequest instanceof okhttp3.Request) { - okhttp3.Request request = (okhttp3.Request) originalRequest; - HttpUrl url = request.url(); - span = HttpClientHelper.startHttpClientSpan(parent, request.method(), url.toString(), url.scheme(), - OkHttpClientHelper.computeHostName(url.host()), url.port()); - if (span != null) { - span.activate(); - if (headerSetterHelperManager != null) { - TextHeaderSetter headerSetter = headerSetterHelperManager.getForClassLoaderOfClass(Request.class); - if (headerSetter != null) { - Request.Builder builder = ((okhttp3.Request) originalRequest).newBuilder(); - span.propagateTraceContext(builder, headerSetter); - originalRequest = builder.build(); - } + okhttp3.Request request = (okhttp3.Request) originalRequest; + HttpUrl url = request.url(); + Span span = HttpClientHelper.startHttpClientSpan(parent, request.method(), url.toString(), url.scheme(), + OkHttpClientHelper.computeHostName(url.host()), url.port()); + if (span != null) { + spanTls.set(span); + span.activate(); + if (headerSetterHelperManager != null) { + TextHeaderSetter headerSetter = headerSetterHelperManager.getForClassLoaderOfClass(Request.class); + if (headerSetter != null) { + Request.Builder builder = ((okhttp3.Request) originalRequest).newBuilder(); + span.propagateTraceContext(builder, headerSetter); + return builder.build(); } } } + return originalRequest; } - @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) + @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class, inline = false) public static void onAfterExecute(@Advice.Return @Nullable okhttp3.Response response, @Advice.Thrown @Nullable Throwable t) { - final AbstractSpan active = getActive(); - if (active != null && active.isExit()) { + final Span span = spanTls.getAndRemove(); + if (span != null) { try { if (response != null) { int statusCode = response.code(); - ((Span) active).getContext().getHttp().withStatusCode(statusCode); + span.getContext().getHttp().withStatusCode(statusCode); } - active.captureException(t); + span.captureException(t); } finally { - active.deactivate().end(); + span.deactivate().end(); } } } diff --git a/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttpClientAsyncInstrumentation.java b/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttpClientAsyncInstrumentation.java index 3c539feca6..710163eb89 100644 --- a/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttpClientAsyncInstrumentation.java +++ b/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttpClientAsyncInstrumentation.java @@ -26,6 +26,9 @@ import co.elastic.apm.agent.bci.HelperClassManager; import co.elastic.apm.agent.bci.VisibleForAdvice; +import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignTo; +import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToArgument; +import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToField; import co.elastic.apm.agent.http.client.HttpClientHelper; import co.elastic.apm.agent.impl.ElasticApmTracer; import co.elastic.apm.agent.impl.transaction.AbstractSpan; @@ -38,7 +41,6 @@ import net.bytebuddy.asm.Advice; import net.bytebuddy.description.method.MethodDescription; import net.bytebuddy.description.type.TypeDescription; -import net.bytebuddy.implementation.bytecode.assign.Assigner; import net.bytebuddy.matcher.ElementMatcher; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -78,25 +80,28 @@ public OkHttpClientAsyncInstrumentation(ElasticApmTracer tracer) { @VisibleForAdvice public static class OkHttpClient3ExecuteAdvice { - @Advice.OnMethodEnter(suppress = Throwable.class) - private static void onBeforeEnqueue(@Advice.Origin Class clazz, - @Advice.FieldValue(value = "originalRequest", typing = Assigner.Typing.DYNAMIC, readOnly = false) @Nullable Request originalRequest, - @Advice.Argument(value = 0, readOnly = false) @Nullable Callback callback, - @Advice.Local("span") Span span) { + @AssignTo( + fields = @AssignToField(index = 0, value = "originalRequest"), + arguments = @AssignToArgument(index = 1, value = 0) + ) + @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) + public static Object[] onBeforeEnqueue(@Advice.Origin Class clazz, + @Advice.FieldValue("originalRequest") @Nullable Request originalRequest, + @Advice.Argument(0) @Nullable Callback callback) { if (tracer == null || tracer.getActive() == null || callbackWrapperCreator == null) { - return; + return new Object[] {originalRequest, callback, null}; } final WrapperCreator wrapperCreator = callbackWrapperCreator.getForClassLoaderOfClass(clazz); if (originalRequest == null || callback == null || wrapperCreator == null) { - return; + return new Object[] {originalRequest, callback, null}; } final AbstractSpan parent = tracer.getActive(); Request request = originalRequest; URL url = request.url(); - span = HttpClientHelper.startHttpClientSpan(parent, request.method(), url.toString(), url.getProtocol(), + Span span = HttpClientHelper.startHttpClientSpan(parent, request.method(), url.toString(), url.getProtocol(), OkHttpClientHelper.computeHostName(url.getHost()), url.getPort()); if (span != null) { span.activate(); @@ -110,10 +115,12 @@ private static void onBeforeEnqueue(@Advice.Origin Class clazz, } callback = wrapperCreator.wrap(callback, span); } + return new Object[] {originalRequest, callback, span}; } - @Advice.OnMethodExit(suppress = Throwable.class) - private static void onAfterEnqueue(@Advice.Local("span") @Nullable Span span) { + @Advice.OnMethodExit(suppress = Throwable.class, inline = false) + public static void onAfterEnqueue(@Advice.Enter @Nullable Object[] enter) { + Span span = enter != null ? (Span) enter[2] : null; if (span != null) { span.deactivate(); } diff --git a/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttpClientInstrumentation.java b/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttpClientInstrumentation.java index 2f066d6a07..7097bb7ae6 100644 --- a/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttpClientInstrumentation.java +++ b/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttpClientInstrumentation.java @@ -25,11 +25,13 @@ package co.elastic.apm.agent.okhttp; import co.elastic.apm.agent.bci.VisibleForAdvice; +import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToField; import co.elastic.apm.agent.http.client.HttpClientHelper; import co.elastic.apm.agent.impl.ElasticApmTracer; import co.elastic.apm.agent.impl.transaction.AbstractSpan; import co.elastic.apm.agent.impl.transaction.Span; import co.elastic.apm.agent.impl.transaction.TextHeaderSetter; +import co.elastic.apm.agent.util.RemoveOnGetThreadLocal; import com.squareup.okhttp.HttpUrl; import com.squareup.okhttp.Request; import net.bytebuddy.asm.Advice; @@ -56,43 +58,42 @@ public Class getAdviceClass() { @VisibleForAdvice public static class OkHttpClientExecuteAdvice { + private final static RemoveOnGetThreadLocal spanTls = new RemoveOnGetThreadLocal<>(); - @Advice.OnMethodEnter(suppress = Throwable.class) - private static void onBeforeExecute(@Advice.FieldValue(value = "originalRequest", typing = Assigner.Typing.DYNAMIC, readOnly = false) @Nullable Object originalRequest, - @Advice.Local("span") Span span) { + @Nullable + @AssignToField(value = "originalRequest", typing = Assigner.Typing.DYNAMIC) + @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) + public static Object onBeforeExecute(@Advice.FieldValue("originalRequest") @Nullable Object originalRequest) { - if (tracer == null || tracer.getActive() == null) { - return; + if (tracer == null || tracer.getActive() == null || !(originalRequest instanceof com.squareup.okhttp.Request)) { + return originalRequest; } - final AbstractSpan parent = tracer.getActive(); - if (originalRequest == null) { - return; - } + final AbstractSpan parent = tracer.getActive(); - if (originalRequest instanceof com.squareup.okhttp.Request) { - com.squareup.okhttp.Request request = (com.squareup.okhttp.Request) originalRequest; - HttpUrl httpUrl = request.httpUrl(); - span = HttpClientHelper.startHttpClientSpan(parent, request.method(), httpUrl.toString(), httpUrl.scheme(), - OkHttpClientHelper.computeHostName(httpUrl.host()), httpUrl.port()); - if (span != null) { - span.activate(); - if (headerSetterHelperManager != null) { - TextHeaderSetter headerSetter = headerSetterHelperManager.getForClassLoaderOfClass(Request.class); - if (headerSetter != null) { - Request.Builder builder = ((com.squareup.okhttp.Request) originalRequest).newBuilder(); - span.propagateTraceContext(builder, headerSetter); - originalRequest = builder.build(); - } + com.squareup.okhttp.Request request = (com.squareup.okhttp.Request) originalRequest; + HttpUrl httpUrl = request.httpUrl(); + Span span = HttpClientHelper.startHttpClientSpan(parent, request.method(), httpUrl.toString(), httpUrl.scheme(), + OkHttpClientHelper.computeHostName(httpUrl.host()), httpUrl.port()); + if (span != null) { + spanTls.set(span); + span.activate(); + if (headerSetterHelperManager != null) { + TextHeaderSetter headerSetter = headerSetterHelperManager.getForClassLoaderOfClass(Request.class); + if (headerSetter != null) { + Request.Builder builder = ((com.squareup.okhttp.Request) originalRequest).newBuilder(); + span.propagateTraceContext(builder, headerSetter); + return builder.build(); } } } + return originalRequest; } - @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) + @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class, inline = false) public static void onAfterExecute(@Advice.Return @Nullable com.squareup.okhttp.Response response, - @Advice.Local("span") @Nullable Span span, @Advice.Thrown @Nullable Throwable t) { + Span span = spanTls.getAndRemove(); if (span != null) { try { if (response != null) { diff --git a/apm-agent-plugins/apm-opentracing-plugin/src/main/java/co/elastic/apm/agent/opentracing/impl/ExternalSpanContextInstrumentation.java b/apm-agent-plugins/apm-opentracing-plugin/src/main/java/co/elastic/apm/agent/opentracing/impl/ExternalSpanContextInstrumentation.java index 5b72b19ee1..2f97fb2b19 100644 --- a/apm-agent-plugins/apm-opentracing-plugin/src/main/java/co/elastic/apm/agent/opentracing/impl/ExternalSpanContextInstrumentation.java +++ b/apm-agent-plugins/apm-opentracing-plugin/src/main/java/co/elastic/apm/agent/opentracing/impl/ExternalSpanContextInstrumentation.java @@ -25,6 +25,8 @@ package co.elastic.apm.agent.opentracing.impl; import co.elastic.apm.agent.bci.VisibleForAdvice; +import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToField; +import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToReturn; import co.elastic.apm.agent.impl.transaction.TraceContext; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.method.MethodDescription; @@ -65,17 +67,27 @@ public ToTraceIdInstrumentation() { super(named("toTraceId")); } - @SuppressWarnings("Duplicates") - @Advice.OnMethodExit(suppress = Throwable.class) - public static void toTraceId(@Advice.FieldValue(value = "textMap", typing = Assigner.Typing.DYNAMIC) @Nullable Iterable> textMap, - @Advice.FieldValue(value = "childTraceContext", typing = Assigner.Typing.DYNAMIC, readOnly = false) @Nullable TraceContext childTraceContext, - @Advice.Return(readOnly = false) String traceId) { - if (textMap != null && childTraceContext == null) { - childTraceContext = parseTextMap(textMap); + @Nullable + @AssignToField(value = "childTraceContext") + @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) + public static TraceContext toTraceId(@Advice.FieldValue(value = "textMap", typing = Assigner.Typing.DYNAMIC) @Nullable Iterable> textMap, + @Advice.FieldValue(value = "childTraceContext", typing = Assigner.Typing.DYNAMIC) @Nullable TraceContext childTraceContext) { + if (textMap == null) { + return childTraceContext; } - if (childTraceContext != null) { - traceId = childTraceContext.getTraceId().toString(); + return parseTextMap(textMap); + + } + + + @Nullable + @AssignToReturn + @Advice.OnMethodExit(suppress = Throwable.class, inline = false) + public static String onExit(@Advice.FieldValue(value = "childTraceContext", typing = Assigner.Typing.DYNAMIC) @Nullable TraceContext childTraceContext) { + if (childTraceContext == null) { + return null; } + return childTraceContext.getTraceId().toString(); } } @@ -85,17 +97,25 @@ public ToSpanIdInstrumentation() { super(named("toSpanId")); } - @SuppressWarnings("Duplicates") - @Advice.OnMethodExit(suppress = Throwable.class) - public static void toSpanId(@Advice.FieldValue(value = "textMap", typing = Assigner.Typing.DYNAMIC) @Nullable Iterable> textMap, - @Advice.FieldValue(value = "childTraceContext", typing = Assigner.Typing.DYNAMIC, readOnly = false) @Nullable TraceContext childTraceContext, - @Advice.Return(readOnly = false) String spanId) { - if (textMap != null && childTraceContext == null) { - childTraceContext = parseTextMap(textMap); + @Nullable + @AssignToField(value = "childTraceContext") + @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) + public static TraceContext toSpanId(@Advice.FieldValue(value = "textMap", typing = Assigner.Typing.DYNAMIC) @Nullable Iterable> textMap, + @Advice.FieldValue(value = "childTraceContext", typing = Assigner.Typing.DYNAMIC) @Nullable TraceContext childTraceContext) { + if (textMap == null) { + return childTraceContext; } - if (childTraceContext != null) { - spanId = childTraceContext.getParentId().toString(); + return parseTextMap(textMap); + } + + @Nullable + @AssignToReturn + @Advice.OnMethodExit(suppress = Throwable.class, inline = false) + public static String onExit(@Advice.FieldValue(value = "childTraceContext", typing = Assigner.Typing.DYNAMIC) @Nullable TraceContext childTraceContext) { + if (childTraceContext == null) { + return null; } + return childTraceContext.getParentId().toString(); } } From 543f3aedc467ab45dcdeaa223e2ff0dc40e6d2e7 Mon Sep 17 00:00:00 2001 From: Felix Barnsteiner Date: Sat, 6 Jun 2020 15:49:45 +0200 Subject: [PATCH 07/35] Use indy dispatcher for Servlet and JDBC plugins - Add AssignTo annotation to assign to different targets by returning an Object[] - Add GlobalState annotation - Create ThreadLocalRegistry to easily construct global thread locals - Patch class file version to at least 51 (Java 7) in order to be able to insert invokedynamic instructions - Load the whole package of the Advice class from a dedicated plugin classloader that's a child of the classloader of the instrumented class - Depend on byte-buddy-dep and manually shade asm so that we can use asm-commons --- apm-agent-core/pom.xml | 7 +- .../apm/agent/bci/ElasticApmAgent.java | 105 ++++++++++++++++++ .../agent/bci/ElasticApmInstrumentation.java | 4 + .../co/elastic/apm/agent/bci/GlobalState.java | 23 ++++ .../apm/agent/bci/HelperClassManager.java | 67 +++++++++++ .../MinimumClassFileVersionValidator.java | 6 +- .../bci/bytebuddy/postprocessor/AssignTo.java | 24 ++++ .../AssignToFieldPostProcessorFactory.java | 87 --------------- .../AssignToPostProcessorFactory.java | 87 +++++++++++++-- .../AssignToReturnPostProcessorFactory.java | 61 ---------- .../threadlocal/RemoveOnGetThreadLocal.java | 60 ++++++++++ .../threadlocal/ThreadLocalRegistry.java | 51 +++++++++ .../apm/agent/threadlocal/package-info.java | 28 +++++ .../co/elastic/apm/agent/util/CallDepth.java | 75 +++++++++++++ .../apm/agent/util/PackageScanner.java | 101 +++++++++++++++++ .../agent/util/RemoveOnGetThreadLocal.java | 22 ---- .../apm/agent/bci/InstrumentationTest.java | 8 +- ...asticsearchClientAsyncInstrumentation.java | 2 +- ...asticsearchClientAsyncInstrumentation.java | 2 +- .../agent/jdbc/ConnectionInstrumentation.java | 19 +--- .../apm/agent/jdbc/JdbcInstrumentation.java | 24 +--- .../agent/jdbc/StatementInstrumentation.java | 96 ++++++---------- .../agent/jdbc/helper/JdbcGlobalState.java | 52 +++++++++ .../apm/agent/jdbc/helper/JdbcHelper.java | 72 ------------ .../apm/agent/jdbc/helper/JdbcHelperImpl.java | 55 +++++---- .../jdbc/AbstractJdbcInstrumentationTest.java | 13 +-- .../okhttp/OkHttp3ClientInstrumentation.java | 2 +- .../okhttp/OkHttpClientInstrumentation.java | 2 +- .../AbstractServletInstrumentation.java | 7 +- .../agent/servlet/AsyncInstrumentation.java | 38 +------ .../servlet/FilterChainInstrumentation.java | 5 - ...RequestStreamRecordingInstrumentation.java | 29 +---- .../apm/agent/servlet/ServletApiAdvice.java | 76 ++++++------- ...elperImpl.java => ServletGlobalState.java} | 25 +++-- .../agent/servlet/ServletInstrumentation.java | 27 ----- .../servlet/ServletTransactionHelper.java | 9 +- .../ServletVersionInstrumentation.java | 19 ++-- ... => ServletTransactionCreationHelper.java} | 8 +- .../agent/servlet/AbstractServletTest.java | 2 +- .../apm/agent/servlet/AsyncServletTest.java | 4 +- ...ctExceptionHandlerInstrumentationTest.java | 2 +- ...tractViewRenderingInstrumentationTest.java | 2 +- elastic-apm-agent/pom.xml | 4 + .../co/elastic/apm/servlet/WildFlyIT.java | 2 + pom.xml | 3 +- 45 files changed, 851 insertions(+), 566 deletions(-) create mode 100644 apm-agent-core/src/main/java/co/elastic/apm/agent/bci/GlobalState.java delete mode 100644 apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToFieldPostProcessorFactory.java delete mode 100644 apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToReturnPostProcessorFactory.java create mode 100644 apm-agent-core/src/main/java/co/elastic/apm/agent/threadlocal/RemoveOnGetThreadLocal.java create mode 100644 apm-agent-core/src/main/java/co/elastic/apm/agent/threadlocal/ThreadLocalRegistry.java create mode 100644 apm-agent-core/src/main/java/co/elastic/apm/agent/threadlocal/package-info.java create mode 100644 apm-agent-core/src/main/java/co/elastic/apm/agent/util/CallDepth.java create mode 100644 apm-agent-core/src/main/java/co/elastic/apm/agent/util/PackageScanner.java delete mode 100644 apm-agent-core/src/main/java/co/elastic/apm/agent/util/RemoveOnGetThreadLocal.java create mode 100644 apm-agent-plugins/apm-jdbc-plugin/src/main/java/co/elastic/apm/agent/jdbc/helper/JdbcGlobalState.java delete mode 100644 apm-agent-plugins/apm-jdbc-plugin/src/main/java/co/elastic/apm/agent/jdbc/helper/JdbcHelper.java rename apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/{helper/InputStreamFactoryHelperImpl.java => ServletGlobalState.java} (63%) rename apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/helper/{ServletTransactionCreationHelperImpl.java => ServletTransactionCreationHelper.java} (91%) diff --git a/apm-agent-core/pom.xml b/apm-agent-core/pom.xml index c3a6d1ecaf..436d132ce8 100644 --- a/apm-agent-core/pom.xml +++ b/apm-agent-core/pom.xml @@ -62,9 +62,14 @@ net.bytebuddy - byte-buddy + byte-buddy-dep ${version.byte-buddy} + + org.ow2.asm + asm-tree + 8.0.1 + org.hdrhistogram HdrHistogram diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/ElasticApmAgent.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/ElasticApmAgent.java index 817ba58012..196ecf297e 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/ElasticApmAgent.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/ElasticApmAgent.java @@ -41,6 +41,7 @@ import co.elastic.apm.agent.matcher.WildcardMatcher; import co.elastic.apm.agent.util.DependencyInjectingServiceLoader; import co.elastic.apm.agent.util.ExecutorUtils; +import co.elastic.apm.agent.util.PackageScanner; import co.elastic.apm.agent.util.ThreadUtils; import com.blogspot.mydailyjava.weaklockfree.WeakConcurrentMap; import net.bytebuddy.ByteBuddy; @@ -48,15 +49,25 @@ import net.bytebuddy.agent.builder.AgentBuilder.RedefinitionStrategy; import net.bytebuddy.agent.builder.ResettableClassFileTransformer; import net.bytebuddy.asm.Advice; +import net.bytebuddy.asm.AsmVisitorWrapper; import net.bytebuddy.description.NamedElement; +import net.bytebuddy.description.field.FieldDescription; +import net.bytebuddy.description.field.FieldList; import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.description.method.MethodList; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.dynamic.ClassFileLocator; import net.bytebuddy.dynamic.DynamicType; +import net.bytebuddy.dynamic.loading.ByteArrayClassLoader; import net.bytebuddy.dynamic.scaffold.TypeValidation; +import net.bytebuddy.implementation.Implementation; import net.bytebuddy.matcher.ElementMatcher; import net.bytebuddy.pool.TypePool; import net.bytebuddy.utility.JavaModule; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.commons.JSRInlinerAdapter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.stagemonitor.configuration.ConfigurationOption; @@ -66,6 +77,10 @@ import java.io.File; import java.io.IOException; import java.lang.instrument.Instrumentation; +import java.lang.invoke.ConstantCallSite; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; import java.lang.reflect.Constructor; import java.security.ProtectionDomain; import java.util.ArrayList; @@ -73,6 +88,7 @@ import java.util.Collections; import java.util.HashSet; import java.util.List; +import java.util.Objects; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; @@ -83,9 +99,12 @@ import static co.elastic.apm.agent.bci.ElasticApmInstrumentation.tracer; import static co.elastic.apm.agent.bci.bytebuddy.ClassLoaderNameMatcher.classLoaderWithName; import static co.elastic.apm.agent.bci.bytebuddy.ClassLoaderNameMatcher.isReflectionClassLoader; +import static java.lang.invoke.MethodHandles.dropArguments; +import static java.lang.invoke.MethodHandles.zero; import static net.bytebuddy.asm.Advice.ExceptionHandler.Default.PRINTING; import static net.bytebuddy.matcher.ElementMatchers.any; import static net.bytebuddy.matcher.ElementMatchers.is; +import static net.bytebuddy.matcher.ElementMatchers.isAnnotatedWith; import static net.bytebuddy.matcher.ElementMatchers.isInterface; import static net.bytebuddy.matcher.ElementMatchers.nameContains; import static net.bytebuddy.matcher.ElementMatchers.nameEndsWith; @@ -93,6 +112,7 @@ import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.none; import static net.bytebuddy.matcher.ElementMatchers.not; +import static org.objectweb.asm.ClassWriter.COMPUTE_FRAMES; public class ElasticApmAgent { @@ -286,6 +306,47 @@ public boolean matches(TypeDescription typeDescription, ClassLoader classLoader, } } }) + .transform(new AgentBuilder.Transformer() { + @Override + public DynamicType.Builder transform(DynamicType.Builder builder, TypeDescription typeDescription, ClassLoader classLoader, JavaModule javaModule) { + return builder.visit(new AsmVisitorWrapper.AbstractBase() { + @Override + public ClassVisitor wrap(TypeDescription typeDescription, ClassVisitor classVisitor, Implementation.Context context, + TypePool typePool, FieldList fieldList, MethodList methodList, int writerFlags, int readerFlags) { + return new ClassVisitor(Opcodes.ASM7, classVisitor) { + private boolean patchVersion; + + @Override + public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { + if (version < Opcodes.V1_7) { + patchVersion = true; + // up-patching the class file verison to version 7 in order to support advice dispatching via invoke dynamic + version = Opcodes.V1_7; + } + super.visit(version, access, name, signature, superName, interfaces); + } + + @Override + public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { + final MethodVisitor methodVisitor = super.visitMethod(access, name, descriptor, signature, exceptions); + if (patchVersion) { + return new JSRInlinerAdapter(methodVisitor, access, name, descriptor, signature, exceptions); + } else { + return methodVisitor; + } + } + }; + } + + @Override + public int mergeWriter(int flags) { + // class files with version < Java 7 don't require a stack frame map + // as we're patching the version to at least 7, we have to compute the frames + return flags | COMPUTE_FRAMES; + } + }); + } + }) .transform(getTransformer(tracer, instrumentation, logger, methodMatcher)) .transform(new AgentBuilder.Transformer() { @Override @@ -306,6 +367,13 @@ private static AgentBuilder.Transformer.ForAdvice getTransformer(final ElasticAp if (offsetMapping != null) { withCustomMapping = withCustomMapping.bind(offsetMapping); } + if (instrumentation.indyDispatch()) { + try { + withCustomMapping = withCustomMapping.bootstrap(ElasticApmAgent.class.getMethod("bootstrap", MethodHandles.Lookup.class, String.class, MethodType.class, String.class, Class.class, MethodHandle.class, String.class, int.class)); + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } + } return new AgentBuilder.Transformer.ForAdvice(withCustomMapping) .advice(new ElementMatcher() { @Override @@ -333,6 +401,41 @@ public boolean matches(MethodDescription target) { .withExceptionHandler(PRINTING); } + private static final ConcurrentMap> classesByPackage = new ConcurrentHashMap<>(); + + public static ConstantCallSite bootstrap(MethodHandles.Lookup lookup, + String adviceMethodName, + MethodType adviceMethodType, + String adviceClassName, + Class instrumentedType, + MethodHandle instrumentedMethod, + String instrumentedMethodName, + int enter) { + try { + Class adviceClass = Class.forName(adviceClassName); + String packageName = adviceClass.getPackage().getName(); + List helperClasses = classesByPackage.get(packageName); + if (helperClasses == null) { + classesByPackage.putIfAbsent(packageName, PackageScanner.getClassNames(packageName)); + helperClasses = classesByPackage.get(packageName); + } + ClassLoader helperClassLoader = HelperClassManager.ForDispatcher.inject(lookup.lookupClass().getClassLoader(), instrumentedType.getProtectionDomain(), helperClasses, isAnnotatedWith(named(GlobalState.class.getName()))); + if (helperClassLoader != null) { + Class adviceInHelperCL = helperClassLoader.loadClass(adviceClassName); + MethodHandle methodHandle = MethodHandles.lookup().findStatic(adviceInHelperCL, adviceMethodName, adviceMethodType); + return new ConstantCallSite(methodHandle); + } + } catch (Exception e) { + e.printStackTrace(); + } + return new ConstantCallSite(empty(adviceMethodType)); + } + + public static MethodHandle empty(MethodType type) { + Objects.requireNonNull(type); + return dropArguments(zero(type.returnType()), 0, type.parameterList()); + } + private static MatcherTimer getOrCreateTimer(Class adviceClass) { final String name = adviceClass.getName(); MatcherTimer timer = matcherTimers.get(name); @@ -428,6 +531,8 @@ private static AgentBuilder getAgentBuilder(final ByteBuddy byteBuddy, final Cor ? new SoftlyReferencingTypePoolCache(TypePool.Default.ReaderMode.FAST, 1, isReflectionClassLoader()) : AgentBuilder.PoolStrategy.Default.FAST) .ignore(any(), isReflectionClassLoader()) + // avoids instrumenting classes from helper class loaders + .or(any(), classLoaderWithName(ByteArrayClassLoader.ChildFirst.class.getName())) .or(any(), classLoaderWithName("org.codehaus.groovy.runtime.callsite.CallSiteClassLoader")) // ideally, those bootstrap classpath inclusions should be set at plugin level, see issue #952 .or(nameStartsWith("java.") diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/ElasticApmInstrumentation.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/ElasticApmInstrumentation.java index c4db3392a3..17c6621a74 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/ElasticApmInstrumentation.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/ElasticApmInstrumentation.java @@ -194,4 +194,8 @@ public Advice.OffsetMapping.Factory getOffsetMapping() { public void onTypeMatch(TypeDescription typeDescription, ClassLoader classLoader, ProtectionDomain protectionDomain, @Nullable Class classBeingRedefined) { } + + public boolean indyDispatch() { + return false; + } } diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/GlobalState.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/GlobalState.java new file mode 100644 index 0000000000..8a03e8bd3b --- /dev/null +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/GlobalState.java @@ -0,0 +1,23 @@ +package co.elastic.apm.agent.bci; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotating a class with {@link GlobalState} excludes it from being loaded by the plugin class loader. + * It will instead be loaded by the agent class loader, which is currently the bootstrap class loader, although that is subject to change. + * This will make it's static variables globally available instead of being local to the plugin class loader. + *

+ * Normally, all classes within an instrumentation plugin are loaded from a dedicated class loader + * that is the child of the class loader that contains the instrumented classes. + * If there are multiple class loaders that are instrumented with a given instrumentation plugin, + * the instrumentation classes will also be loaded by multiple class loaders. + * The effect of that is that state added to static variables in one class loader does not affect the static variable in other class loaders. + *

+ */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface GlobalState { +} diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/HelperClassManager.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/HelperClassManager.java index d06a6ac705..54c7355f78 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/HelperClassManager.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/HelperClassManager.java @@ -30,7 +30,10 @@ import net.bytebuddy.dynamic.ClassFileLocator; import net.bytebuddy.dynamic.loading.ByteArrayClassLoader; import net.bytebuddy.dynamic.loading.ClassInjector; +import net.bytebuddy.dynamic.loading.MultipleParentClassLoader; import net.bytebuddy.dynamic.loading.PackageDefinitionStrategy; +import net.bytebuddy.matcher.ElementMatcher; +import net.bytebuddy.pool.TypePool; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -40,8 +43,10 @@ import java.security.ProtectionDomain; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.WeakHashMap; @@ -271,6 +276,68 @@ private synchronized T loadAndReferenceHelper(Class classOfTargetClassLoader) } } + public static class ForDispatcher { + + private static final Map, WeakReference>> alreadyInjected = new WeakHashMap, WeakReference>>(); + + /** + * Creates an isolated CL that has two parents: the target class loader and the agent CL. + * The agent class loader is currently the bootstrap CL but in the future it will be an isolated CL that is a child of the bootstrap CL. + */ + @Nullable + public synchronized static ClassLoader inject(@Nullable ClassLoader targetClassLoader, @Nullable ProtectionDomain protectionDomain, List classesToInject, ElementMatcher exclusionMatcher) throws Exception { + classesToInject = new ArrayList<>(classesToInject); + + Map, WeakReference> injectedClasses = getOrCreateInjectedClasses(targetClassLoader); + if (injectedClasses.containsKey(classesToInject)) { + return injectedClasses.get(classesToInject).get(); + } + + List classesToInjectCopy = new ArrayList<>(classesToInject.size()); + TypePool pool = new TypePool.Default(TypePool.CacheProvider.NoOp.INSTANCE, ClassFileLocator.ForClassLoader.ofSystemLoader(), TypePool.Default.ReaderMode.FAST); + for (Iterator iterator = classesToInject.iterator(); iterator.hasNext(); ) { + String className = iterator.next(); + if (!exclusionMatcher.matches(pool.describe(className).resolve())) { + classesToInjectCopy.add(className); + } + } + logger.debug("Creating helper class loader for {} containing {}", targetClassLoader, classesToInjectCopy); + + ClassLoader parent = getHelperClassLoaderParent(targetClassLoader); + Map typeDefinitions = getTypeDefinitions(classesToInjectCopy); + // child first semantics are important here as the helper CL contains classes that are also present in the agent CL + ClassLoader helperCL = new ByteArrayClassLoader.ChildFirst(parent, true, typeDefinitions, ByteArrayClassLoader.PersistenceHandler.MANIFEST); + injectedClasses.put(classesToInject, new WeakReference<>(helperCL)); + + + return helperCL; + } + + private static Map, WeakReference> getOrCreateInjectedClasses(@Nullable ClassLoader targetClassLoader) { + Map, WeakReference> injectedClasses = alreadyInjected.get(targetClassLoader); + if (injectedClasses == null) { + injectedClasses = new HashMap<>(); + alreadyInjected.put(targetClassLoader, injectedClasses); + } + return injectedClasses; + } + + @Nullable + private static ClassLoader getHelperClassLoaderParent(@Nullable ClassLoader targetClassLoader) { + ClassLoader agentClassLoader = HelperClassManager.class.getClassLoader(); + if (agentClassLoader != null && agentClassLoader != ClassLoader.getSystemClassLoader()) { + // future world: when the agent is loaded from an isolated class loader + // the helper class loader has both, the agent class loader and the target class loader as the parent + return new MultipleParentClassLoader(Arrays.asList(agentClassLoader, targetClassLoader)); + } + return targetClassLoader; + } + + public synchronized static void clear() { + alreadyInjected.clear(); + } + } + static Class injectClass(@Nullable ClassLoader targetClassLoader, @Nullable ProtectionDomain pd, String className, boolean isBootstrapClass) throws IOException, ClassNotFoundException { if (targetClassLoader == null) { if (isBootstrapClass) { diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/MinimumClassFileVersionValidator.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/MinimumClassFileVersionValidator.java index 3949197d78..76e83380f4 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/MinimumClassFileVersionValidator.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/MinimumClassFileVersionValidator.java @@ -11,9 +11,9 @@ * the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -31,9 +31,9 @@ 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.pool.TypePool; import net.bytebuddy.utility.OpenedClassReader; +import org.objectweb.asm.ClassVisitor; public enum MinimumClassFileVersionValidator implements AsmVisitorWrapper { diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignTo.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignTo.java index 07c7e78796..1470fbddc2 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignTo.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignTo.java @@ -1,3 +1,27 @@ +/*- + * #%L + * Elastic APM Java agent + * %% + * Copyright (C) 2018 - 2020 Elastic and contributors + * %% + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * #L% + */ package co.elastic.apm.agent.bci.bytebuddy.postprocessor; import java.lang.annotation.ElementType; diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToFieldPostProcessorFactory.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToFieldPostProcessorFactory.java deleted file mode 100644 index def042d434..0000000000 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToFieldPostProcessorFactory.java +++ /dev/null @@ -1,87 +0,0 @@ -/*- - * #%L - * Elastic APM Java agent - * %% - * Copyright (C) 2018 - 2020 Elastic and contributors - * %% - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * #L% - */ -package co.elastic.apm.agent.bci.bytebuddy.postprocessor; - -import net.bytebuddy.asm.Advice; -import net.bytebuddy.description.annotation.AnnotationList; -import net.bytebuddy.description.field.FieldDescription; -import net.bytebuddy.description.method.MethodDescription; -import net.bytebuddy.description.type.TypeDescription; -import net.bytebuddy.dynamic.TargetType; -import net.bytebuddy.dynamic.scaffold.FieldLocator; -import net.bytebuddy.implementation.bytecode.StackManipulation; -import net.bytebuddy.implementation.bytecode.assign.Assigner; -import net.bytebuddy.implementation.bytecode.member.FieldAccess; -import net.bytebuddy.implementation.bytecode.member.MethodVariableAccess; - -import static net.bytebuddy.matcher.ElementMatchers.annotationType; - -public class AssignToFieldPostProcessorFactory implements Advice.PostProcessor.Factory { - @Override - public Advice.PostProcessor make(final MethodDescription.InDefinedShape adviceMethod, final boolean exit) { - final AnnotationList annotations = adviceMethod.getDeclaredAnnotations().filter(annotationType(AssignToField.class)); - if (!annotations.isEmpty()) { - final AssignToField assignTo = annotations.getOnly().prepare(AssignToField.class).load(); - return new Advice.PostProcessor() { - @Override - public StackManipulation resolve(TypeDescription instrumentedType, MethodDescription instrumentedMethod, Assigner assigner, Advice.ArgumentHandler argumentHandler) { - final FieldDescription field = getFieldLocator(instrumentedType, assignTo).locate(assignTo.value()).getField(); - - if (!field.isStatic() && instrumentedMethod.isStatic()) { - throw new IllegalStateException("Cannot read non-static field " + field + " from static method " + instrumentedMethod); - } else if (instrumentedMethod.isConstructor() && !exit) { - throw new IllegalStateException("Cannot access non-static field before calling constructor: " + instrumentedMethod); - } - - final StackManipulation assign = assigner.assign(adviceMethod.getReturnType(), field.getType(), assignTo.typing()); - if (!assign.isValid()) { - throw new IllegalStateException("Cannot assign " + adviceMethod.getReturnType() + " to " + field.getType()); - } - return new StackManipulation.Compound( - MethodVariableAccess.loadThis(), - MethodVariableAccess.of(adviceMethod.getReturnType()).loadFrom(exit ? argumentHandler.exit() : argumentHandler.enter()), - assign, - FieldAccess.forField(field).write() - ); - } - - }; - } else { - return Advice.PostProcessor.NoOp.INSTANCE; - } - } - - private static FieldLocator getFieldLocator(TypeDescription instrumentedType, AssignToField assignTo) { - if (assignTo.declaringType() == Void.class) { - return new FieldLocator.ForClassHierarchy(instrumentedType); - } else { - final TypeDescription declaringType = TypeDescription.ForLoadedType.of(assignTo.declaringType()); - if (!declaringType.represents(TargetType.class) && !instrumentedType.isAssignableTo(declaringType)) { - throw new IllegalStateException(declaringType + " is no super type of " + instrumentedType); - } - return new FieldLocator.ForExactType(declaringType); - } - } -} diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToPostProcessorFactory.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToPostProcessorFactory.java index 7c25f2f166..c7a599696e 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToPostProcessorFactory.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToPostProcessorFactory.java @@ -33,12 +33,16 @@ import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.dynamic.TargetType; import net.bytebuddy.dynamic.scaffold.FieldLocator; +import net.bytebuddy.implementation.Implementation; import net.bytebuddy.implementation.bytecode.StackManipulation; import net.bytebuddy.implementation.bytecode.assign.Assigner; import net.bytebuddy.implementation.bytecode.collection.ArrayAccess; import net.bytebuddy.implementation.bytecode.constant.IntegerConstant; import net.bytebuddy.implementation.bytecode.member.FieldAccess; import net.bytebuddy.implementation.bytecode.member.MethodVariableAccess; +import org.objectweb.asm.Label; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; import java.util.ArrayList; import java.util.List; @@ -92,7 +96,17 @@ public Advice.PostProcessor make(final MethodDescription.InDefinedShape adviceMe } } } - return new Compound(postProcessors); + return new Advice.PostProcessor() { + @Override + public StackManipulation resolve(TypeDescription typeDescription, MethodDescription methodDescription, Assigner assigner, Advice.ArgumentHandler argumentHandler) { + final Label jumpToIfNull = new Label(); + return new StackManipulation.Compound( + new AddNullCheck(jumpToIfNull, adviceMethod.getReturnType(), MethodVariableAccess.of(adviceMethod.getReturnType()).loadFrom(exit ? argumentHandler.exit() : argumentHandler.enter())), + new AssignToPostProcessorFactory.Compound(postProcessors).resolve(typeDescription, methodDescription, assigner, argumentHandler), + new AddLabel(jumpToIfNull) + ); + } + }; } private Advice.PostProcessor createAssignToReturnPostProcessor(final MethodDescription.InDefinedShape adviceMethod, final boolean exit, final AssignToReturn assignToReturn) { @@ -143,24 +157,31 @@ public StackManipulation resolve(TypeDescription instrumentedType, MethodDescrip if (!returnType.represents(Object[].class)) { throw new IllegalStateException("Advice method has to return Object[] when setting an index"); } + final Label jumpToIfNull = new Label(); + final StackManipulation load = MethodVariableAccess.REFERENCE.loadFrom(exit ? argumentHandler.exit() : argumentHandler.enter()); return new StackManipulation.Compound( + new AddNullCheck(jumpToIfNull, TypeDescription.Generic.OBJECT, load), MethodVariableAccess.loadThis(), - MethodVariableAccess.REFERENCE.loadFrom(exit ? argumentHandler.exit() : argumentHandler.enter()), - IntegerConstant.forValue(assignToField.index()), - ArrayAccess.REFERENCE.load(), + load, + loadArrayElement(assignToField.index()), assigner.assign(TypeDescription.Generic.OBJECT, field.getType(), Assigner.Typing.DYNAMIC), - FieldAccess.forField(field).write() + FieldAccess.forField(field).write(), + new AddLabel(jumpToIfNull) ); } else { final StackManipulation assign = assigner.assign(adviceMethod.getReturnType(), field.getType(), assignToField.typing()); if (!assign.isValid()) { throw new IllegalStateException("Cannot assign " + adviceMethod.getReturnType() + " to " + field.getType()); } + final Label jumpToIfNull = new Label(); + final StackManipulation load = MethodVariableAccess.of(adviceMethod.getReturnType()).loadFrom(exit ? argumentHandler.exit() : argumentHandler.enter()); return new StackManipulation.Compound( + new AddNullCheck(jumpToIfNull, TypeDescription.Generic.OBJECT, load), MethodVariableAccess.loadThis(), - MethodVariableAccess.of(adviceMethod.getReturnType()).loadFrom(exit ? argumentHandler.exit() : argumentHandler.enter()), + load, assign, - FieldAccess.forField(field).write() + FieldAccess.forField(field).write(), + new AddLabel(jumpToIfNull) ); } } @@ -199,6 +220,12 @@ public StackManipulation resolve(TypeDescription instrumentedType, MethodDescrip }; } + private StackManipulation loadArrayElement(int i) { + return new StackManipulation.Compound( + IntegerConstant.forValue(i), + ArrayAccess.REFERENCE.load()); + } + public static class Compound implements Advice.PostProcessor { /** @@ -229,4 +256,50 @@ public StackManipulation resolve(TypeDescription instrumentedType, return new StackManipulation.Compound(stackManipulations); } } + + private static class AddNullCheck implements StackManipulation { + private final Label jumpToIfNull; + private final TypeDescription.Generic type; + private final StackManipulation load; + + public AddNullCheck(Label jumpToIfNull, TypeDescription.Generic type, StackManipulation load) { + this.jumpToIfNull = jumpToIfNull; + this.type = type; + this.load = load; + } + + @Override + public boolean isValid() { + return true; + } + + @Override + public Size apply(MethodVisitor methodVisitor, Implementation.Context context) { + Size size = new Size(0, 0); + if (!type.isPrimitive()) { + size = size.aggregate(load.apply(methodVisitor, context)); + methodVisitor.visitJumpInsn(Opcodes.IFNULL, jumpToIfNull); + } + return size; + } + } + + private static class AddLabel implements StackManipulation { + private final Label label; + + public AddLabel(Label label) { + this.label = label; + } + + @Override + public boolean isValid() { + return true; + } + + @Override + public Size apply(MethodVisitor methodVisitor, Implementation.Context context) { + methodVisitor.visitLabel(label); + return new Size(0, 0); + } + } } diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToReturnPostProcessorFactory.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToReturnPostProcessorFactory.java deleted file mode 100644 index 35ccd4af7f..0000000000 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToReturnPostProcessorFactory.java +++ /dev/null @@ -1,61 +0,0 @@ -/*- - * #%L - * Elastic APM Java agent - * %% - * Copyright (C) 2018 - 2020 Elastic and contributors - * %% - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * #L% - */ -package co.elastic.apm.agent.bci.bytebuddy.postprocessor; - -import net.bytebuddy.asm.Advice; -import net.bytebuddy.description.annotation.AnnotationList; -import net.bytebuddy.description.method.MethodDescription; -import net.bytebuddy.description.type.TypeDescription; -import net.bytebuddy.implementation.bytecode.StackManipulation; -import net.bytebuddy.implementation.bytecode.assign.Assigner; -import net.bytebuddy.implementation.bytecode.member.MethodVariableAccess; - -import static net.bytebuddy.matcher.ElementMatchers.annotationType; - -public class AssignToReturnPostProcessorFactory implements Advice.PostProcessor.Factory { - @Override - public Advice.PostProcessor make(final MethodDescription.InDefinedShape adviceMethod, final boolean exit) { - final AnnotationList annotations = adviceMethod.getDeclaredAnnotations().filter(annotationType(AssignToReturn.class)); - if (!annotations.isEmpty()) { - final AssignToReturn assignTo = annotations.getOnly().prepare(AssignToReturn.class).load(); - return new Advice.PostProcessor() { - @Override - public StackManipulation resolve(TypeDescription instrumentedType, MethodDescription instrumentedMethod, Assigner assigner, Advice.ArgumentHandler argumentHandler) { - final StackManipulation assign = assigner.assign(adviceMethod.getReturnType(), instrumentedMethod.getReturnType(), assignTo.typing()); - if (!assign.isValid()) { - throw new IllegalStateException("Cannot assign " + adviceMethod.getReturnType() + " to " + instrumentedMethod.getReturnType()); - } - return new StackManipulation.Compound( - MethodVariableAccess.of(adviceMethod.getReturnType()).loadFrom(exit ? argumentHandler.exit() : argumentHandler.enter()), - assign, - MethodVariableAccess.of(instrumentedMethod.getReturnType()).storeAt(argumentHandler.returned()) - ); - } - }; - } else { - return Advice.PostProcessor.NoOp.INSTANCE; - } - } -} diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/threadlocal/RemoveOnGetThreadLocal.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/threadlocal/RemoveOnGetThreadLocal.java new file mode 100644 index 0000000000..6b04f7b7c3 --- /dev/null +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/threadlocal/RemoveOnGetThreadLocal.java @@ -0,0 +1,60 @@ +/*- + * #%L + * Elastic APM Java agent + * %% + * Copyright (C) 2018 - 2020 Elastic and contributors + * %% + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * #L% + */ +package co.elastic.apm.agent.threadlocal; + +import com.blogspot.mydailyjava.weaklockfree.DetachedThreadLocal; + +import javax.annotation.Nullable; + +public class RemoveOnGetThreadLocal extends DetachedThreadLocal { + + @Nullable + private final T defaultValue; + + public RemoveOnGetThreadLocal() { + this(null); + } + + public RemoveOnGetThreadLocal(@Nullable T defaultValue) { + super(Cleaner.INLINE); + this.defaultValue = defaultValue; + } + + @Nullable + public T getAndRemove() { + T value = get(); + if (value != null) { + clear(); + } + return value; + } + + @Override + @Nullable + protected T initialValue(Thread thread) { + return defaultValue; + } + +} diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/threadlocal/ThreadLocalRegistry.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/threadlocal/ThreadLocalRegistry.java new file mode 100644 index 0000000000..43f174a5c4 --- /dev/null +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/threadlocal/ThreadLocalRegistry.java @@ -0,0 +1,51 @@ +/*- + * #%L + * Elastic APM Java agent + * %% + * Copyright (C) 2018 - 2020 Elastic and contributors + * %% + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * #L% + */ +package co.elastic.apm.agent.threadlocal; + + +import javax.annotation.Nullable; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +public class ThreadLocalRegistry { + private static final ConcurrentMap> registry = new ConcurrentHashMap<>(); + + public static RemoveOnGetThreadLocal get(String key) { + return get(key, null); + } + + public static RemoveOnGetThreadLocal get(Class adviceClass, String key, @Nullable T defaultValue) { + return get(adviceClass.getName() + "." + key); + } + + public static RemoveOnGetThreadLocal get(String key, @Nullable T defaultValue) { + RemoveOnGetThreadLocal threadLocal = registry.get(key); + if (threadLocal == null) { + registry.putIfAbsent(key, new RemoveOnGetThreadLocal(defaultValue)); + threadLocal = registry.get(key); + } + return (RemoveOnGetThreadLocal) threadLocal; + } +} diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/threadlocal/package-info.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/threadlocal/package-info.java new file mode 100644 index 0000000000..831582f7c1 --- /dev/null +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/threadlocal/package-info.java @@ -0,0 +1,28 @@ +/*- + * #%L + * Elastic APM Java agent + * %% + * Copyright (C) 2018 - 2020 Elastic and contributors + * %% + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * #L% + */ +@NonnullApi +package co.elastic.apm.agent.threadlocal; + +import co.elastic.apm.agent.annotation.NonnullApi; diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/util/CallDepth.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/util/CallDepth.java new file mode 100644 index 0000000000..8240e5f405 --- /dev/null +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/util/CallDepth.java @@ -0,0 +1,75 @@ +/*- + * #%L + * Elastic APM Java agent + * %% + * Copyright (C) 2018 - 2020 Elastic and contributors + * %% + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * #L% + */ +package co.elastic.apm.agent.util; + +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * A utility that makes it easy to detect nested method calls. + */ +public class CallDepth { + private static final ConcurrentMap registry = new ConcurrentHashMap<>(); + private final ThreadLocal callDepthPerThread = new ThreadLocal(); + + public static CallDepth get(Class adviceClass) { + // we want to return the same CallDepth instance even if the advice class has been loaded from different class loaders + String key = adviceClass.getName(); + CallDepth callDepth = registry.get(key); + if (callDepth == null) { + registry.putIfAbsent(key, new CallDepth()); + callDepth = registry.get(key); + } + return callDepth; + } + + /** + * Gets and increments the call depth counter. + * Returns {@code 0} if this is the outer-most (non-nested) invocation. + * + * @return the call depth before it has been incremented + */ + public int increment() { + AtomicInteger callDepthForCurrentThread = callDepthPerThread.get(); + if (callDepthForCurrentThread == null) { + callDepthForCurrentThread = new AtomicInteger(); + callDepthPerThread.set(callDepthForCurrentThread); + } + return callDepthForCurrentThread.getAndIncrement(); + } + + /** + * Decrements and gets the call depth counter. + * Returns {@code 0} if this is the outer-most (non-nested) invocation. + * + * @return the call depth after it has been incremented + */ + public int decrement() { + int depth = callDepthPerThread.get().decrementAndGet(); + assert depth >= 0; + return depth; + } +} diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/util/PackageScanner.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/util/PackageScanner.java new file mode 100644 index 0000000000..2752c84155 --- /dev/null +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/util/PackageScanner.java @@ -0,0 +1,101 @@ +/*- + * #%L + * Elastic APM Java agent + * %% + * Copyright (C) 2018 - 2020 Elastic and contributors + * %% + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * #L% + */ +package co.elastic.apm.agent.util; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Enumeration; +import java.util.List; + +public class PackageScanner { + + /** + * Returns all class names within a package and sub-packages + * + * @param basePackage the package to scan + * @return all class names within a package and sub-packages + * @throws IOException + * @throws URISyntaxException + */ + public static List getClassNames(final String basePackage) throws IOException, URISyntaxException { + String baseFolderResource = basePackage.replace('.', '/'); + final List classNames = new ArrayList<>(); + Enumeration resources = getResourcesFromAgentClassLoader(baseFolderResource); + while (resources.hasMoreElements()) { + URL resource = resources.nextElement(); + URI uri = resource.toURI(); + if (uri.getScheme().equals("jar")) { + // avoids FileSystemAlreadyExistsException + synchronized (PackageScanner.class) { + try (FileSystem fileSystem = FileSystems.newFileSystem(uri, Collections.emptyMap())) { + final Path basePath = fileSystem.getPath(baseFolderResource).toAbsolutePath(); + classNames.addAll(listClassNames(basePackage, basePath)); + } + } + } else { + final Path basePath = Paths.get(uri); + if (basePath.toString().contains("test-classes")) { + continue; + } + classNames.addAll(listClassNames(basePackage, basePath)); + } + } + return classNames; + } + + private static List listClassNames(final String basePackage, final Path basePath) throws IOException { + final List classNames = new ArrayList<>(); + Files.walkFileTree(basePath, new SimpleFileVisitor() { + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) { + if (file.toString().endsWith(".class")) { + classNames.add(basePackage + "." + basePath.relativize(file).toString().replace('/', '.').replace(".class", "")); + } + return FileVisitResult.CONTINUE; + } + }); + return classNames; + } + + private static Enumeration getResourcesFromAgentClassLoader(String baseFolderResource) throws IOException { + ClassLoader agentCL = PackageScanner.class.getClassLoader(); + if (agentCL == null) { + agentCL = ClassLoader.getSystemClassLoader(); + } + return agentCL.getResources(baseFolderResource); + } +} diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/util/RemoveOnGetThreadLocal.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/util/RemoveOnGetThreadLocal.java deleted file mode 100644 index 8cff409864..0000000000 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/util/RemoveOnGetThreadLocal.java +++ /dev/null @@ -1,22 +0,0 @@ -package co.elastic.apm.agent.util; - -import javax.annotation.Nullable; - -public class RemoveOnGetThreadLocal { - - private final ThreadLocal threadLocal = new ThreadLocal<>(); - - public void set(@Nullable T value) { - threadLocal.set(value); - } - - @Nullable - public T getAndRemove() { - T value = threadLocal.get(); - if (value != null) { - threadLocal.remove(); - } - return value; - } - -} diff --git a/apm-agent-core/src/test/java/co/elastic/apm/agent/bci/InstrumentationTest.java b/apm-agent-core/src/test/java/co/elastic/apm/agent/bci/InstrumentationTest.java index 86832d460f..dd0f089c0b 100644 --- a/apm-agent-core/src/test/java/co/elastic/apm/agent/bci/InstrumentationTest.java +++ b/apm-agent-core/src/test/java/co/elastic/apm/agent/bci/InstrumentationTest.java @@ -43,7 +43,6 @@ import org.apache.commons.math.util.MathUtils; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.stagemonitor.configuration.ConfigurationRegistry; @@ -236,7 +235,6 @@ void testDontInstrumentOldClassFileVersions() { } @Test - @Disabled("this is currently a limitation in Byte Buddy") void testSuppressException() { ElasticApmAgent.initInstrumentation(tracer, ByteBuddyAgent.install(), @@ -282,7 +280,7 @@ private String interceptMe() { public static class TestInstrumentation extends ElasticApmInstrumentation { @AssignToReturn - @Advice.OnMethodExit(inline = false) + @Advice.OnMethodExit public static String onMethodExit() { return "intercepted"; } @@ -454,7 +452,7 @@ public static class AssignToArgumentsInstrumentation extends ElasticApmInstrumen @AssignToArgument(index = 0, value = 1), @AssignToArgument(index = 1, value = 0) }) - @Advice.OnMethodEnter + @Advice.OnMethodEnter(inline = false) public static Object[] onEnter(@Advice.Argument(0) String foo, @Advice.Argument(1) String bar) { return new Object[]{foo, bar}; } @@ -478,7 +476,7 @@ public Collection getInstrumentationGroupNames() { public static class AssignToReturnArrayInstrumentation extends ElasticApmInstrumentation { @AssignTo(returns = @AssignToReturn(index = 0)) - @Advice.OnMethodExit + @Advice.OnMethodExit(inline = false) public static Object[] onEnter(@Advice.Argument(0) String foo, @Advice.Argument(1) String bar) { return new Object[]{foo + bar}; } diff --git a/apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-5_6/src/main/java/co/elastic/apm/agent/es/restclient/v5_6/ElasticsearchClientAsyncInstrumentation.java b/apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-5_6/src/main/java/co/elastic/apm/agent/es/restclient/v5_6/ElasticsearchClientAsyncInstrumentation.java index 20c106a7bb..630e5ddfb9 100644 --- a/apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-5_6/src/main/java/co/elastic/apm/agent/es/restclient/v5_6/ElasticsearchClientAsyncInstrumentation.java +++ b/apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-5_6/src/main/java/co/elastic/apm/agent/es/restclient/v5_6/ElasticsearchClientAsyncInstrumentation.java @@ -29,7 +29,7 @@ import co.elastic.apm.agent.es.restclient.ElasticsearchRestClientInstrumentationHelper; import co.elastic.apm.agent.impl.ElasticApmTracer; import co.elastic.apm.agent.impl.transaction.Span; -import co.elastic.apm.agent.util.RemoveOnGetThreadLocal; +import co.elastic.apm.agent.threadlocal.RemoveOnGetThreadLocal; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.method.MethodDescription; import net.bytebuddy.description.type.TypeDescription; diff --git a/apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-6_4/src/main/java/co/elastic/apm/agent/es/restclient/v6_4/ElasticsearchClientAsyncInstrumentation.java b/apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-6_4/src/main/java/co/elastic/apm/agent/es/restclient/v6_4/ElasticsearchClientAsyncInstrumentation.java index 916ef9f1d7..83fa988279 100644 --- a/apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-6_4/src/main/java/co/elastic/apm/agent/es/restclient/v6_4/ElasticsearchClientAsyncInstrumentation.java +++ b/apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-6_4/src/main/java/co/elastic/apm/agent/es/restclient/v6_4/ElasticsearchClientAsyncInstrumentation.java @@ -29,7 +29,7 @@ import co.elastic.apm.agent.es.restclient.ElasticsearchRestClientInstrumentationHelper; import co.elastic.apm.agent.impl.ElasticApmTracer; import co.elastic.apm.agent.impl.transaction.Span; -import co.elastic.apm.agent.util.RemoveOnGetThreadLocal; +import co.elastic.apm.agent.threadlocal.RemoveOnGetThreadLocal; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.method.MethodDescription; import net.bytebuddy.description.type.TypeDescription; diff --git a/apm-agent-plugins/apm-jdbc-plugin/src/main/java/co/elastic/apm/agent/jdbc/ConnectionInstrumentation.java b/apm-agent-plugins/apm-jdbc-plugin/src/main/java/co/elastic/apm/agent/jdbc/ConnectionInstrumentation.java index 244d0b71dc..44d1604493 100644 --- a/apm-agent-plugins/apm-jdbc-plugin/src/main/java/co/elastic/apm/agent/jdbc/ConnectionInstrumentation.java +++ b/apm-agent-plugins/apm-jdbc-plugin/src/main/java/co/elastic/apm/agent/jdbc/ConnectionInstrumentation.java @@ -25,8 +25,6 @@ package co.elastic.apm.agent.jdbc; import co.elastic.apm.agent.bci.VisibleForAdvice; -import co.elastic.apm.agent.impl.ElasticApmTracer; -import co.elastic.apm.agent.jdbc.helper.JdbcHelper; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.NamedElement; import net.bytebuddy.description.method.MethodDescription; @@ -52,24 +50,11 @@ */ public class ConnectionInstrumentation extends JdbcInstrumentation { - public ConnectionInstrumentation(ElasticApmTracer tracer) { - super(tracer); - } - @VisibleForAdvice - @Advice.OnMethodExit(suppress = Throwable.class) + @Advice.OnMethodExit(suppress = Throwable.class, inline = false) public static void storeSql(@Advice.Return PreparedStatement statement, @Advice.Argument(0) String sql) { - - if (jdbcHelperManager == null) { - return; - } - - JdbcHelper jdbcHelper = jdbcHelperManager.getForClassLoaderOfClass(PreparedStatement.class); - if (jdbcHelper != null) { - jdbcHelper.mapStatementToSql(statement, sql); - } - + jdbcHelper.mapStatementToSql(statement, sql); } @Override diff --git a/apm-agent-plugins/apm-jdbc-plugin/src/main/java/co/elastic/apm/agent/jdbc/JdbcInstrumentation.java b/apm-agent-plugins/apm-jdbc-plugin/src/main/java/co/elastic/apm/agent/jdbc/JdbcInstrumentation.java index 87ad638219..d79fa30bdc 100644 --- a/apm-agent-plugins/apm-jdbc-plugin/src/main/java/co/elastic/apm/agent/jdbc/JdbcInstrumentation.java +++ b/apm-agent-plugins/apm-jdbc-plugin/src/main/java/co/elastic/apm/agent/jdbc/JdbcInstrumentation.java @@ -25,12 +25,8 @@ package co.elastic.apm.agent.jdbc; import co.elastic.apm.agent.bci.ElasticApmInstrumentation; -import co.elastic.apm.agent.bci.HelperClassManager; -import co.elastic.apm.agent.bci.VisibleForAdvice; -import co.elastic.apm.agent.impl.ElasticApmTracer; -import co.elastic.apm.agent.jdbc.helper.JdbcHelper; +import co.elastic.apm.agent.jdbc.helper.JdbcHelperImpl; -import javax.annotation.Nullable; import java.util.Collection; import java.util.Collections; @@ -38,23 +34,15 @@ public abstract class JdbcInstrumentation extends ElasticApmInstrumentation { private static final Collection JDBC_GROUPS = Collections.singleton("jdbc"); - @VisibleForAdvice - @Nullable - public static HelperClassManager jdbcHelperManager = null; - - public JdbcInstrumentation(ElasticApmTracer tracer) { - synchronized (JdbcInstrumentation.class) { - if (jdbcHelperManager == null) { - jdbcHelperManager = HelperClassManager.ForSingleClassLoader.of(tracer, - "co.elastic.apm.agent.jdbc.helper.JdbcHelperImpl", - "co.elastic.apm.agent.jdbc.helper.JdbcHelperImpl$1"); - } - } - } + protected static JdbcHelperImpl jdbcHelper = new JdbcHelperImpl(); @Override public final Collection getInstrumentationGroupNames() { return JDBC_GROUPS; } + @Override + public boolean indyDispatch() { + return true; + } } diff --git a/apm-agent-plugins/apm-jdbc-plugin/src/main/java/co/elastic/apm/agent/jdbc/StatementInstrumentation.java b/apm-agent-plugins/apm-jdbc-plugin/src/main/java/co/elastic/apm/agent/jdbc/StatementInstrumentation.java index d1ffb7c95b..4b5e7c7678 100644 --- a/apm-agent-plugins/apm-jdbc-plugin/src/main/java/co/elastic/apm/agent/jdbc/StatementInstrumentation.java +++ b/apm-agent-plugins/apm-jdbc-plugin/src/main/java/co/elastic/apm/agent/jdbc/StatementInstrumentation.java @@ -27,7 +27,6 @@ import co.elastic.apm.agent.bci.VisibleForAdvice; import co.elastic.apm.agent.impl.ElasticApmTracer; import co.elastic.apm.agent.impl.transaction.Span; -import co.elastic.apm.agent.jdbc.helper.JdbcHelper; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.NamedElement; import net.bytebuddy.description.method.MethodDescription; @@ -55,8 +54,7 @@ public abstract class StatementInstrumentation extends JdbcInstrumentation { private final ElementMatcher methodMatcher; - StatementInstrumentation(ElasticApmTracer tracer, ElementMatcher methodMatcher) { - super(tracer); + StatementInstrumentation(ElementMatcher methodMatcher) { this.methodMatcher = methodMatcher; } @@ -91,7 +89,7 @@ public ElementMatcher getMethodMatcher() { public static class ExecuteWithQueryInstrumentation extends StatementInstrumentation { public ExecuteWithQueryInstrumentation(ElasticApmTracer tracer) { - super(tracer, + super( named("execute").or(named("executeQuery")) .and(takesArgument(0, String.class)) .and(isPublic()) @@ -100,25 +98,20 @@ public ExecuteWithQueryInstrumentation(ElasticApmTracer tracer) { @Nullable @VisibleForAdvice - @Advice.OnMethodEnter(suppress = Throwable.class) + @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) public static Span onBeforeExecute(@Advice.This Statement statement, @Advice.Argument(0) String sql) { - if (tracer == null || jdbcHelperManager == null) { + if (tracer == null) { return null; } - JdbcHelper helperImpl = jdbcHelperManager.getForClassLoaderOfClass(Statement.class); - if (helperImpl == null) { - return null; - } - - return helperImpl.createJdbcSpan(sql, statement, tracer.getActive(), false); + return jdbcHelper.createJdbcSpan(sql, statement, tracer.getActive(), false); } @VisibleForAdvice - @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) + @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class, inline = false) public static void onAfterExecute(@Advice.This Statement statement, @Advice.Enter @Nullable Span span, @Advice.Thrown @Nullable Throwable t) { @@ -149,7 +142,7 @@ public static void onAfterExecute(@Advice.This Statement statement, public static class ExecuteUpdateWithQueryInstrumentation extends StatementInstrumentation { public ExecuteUpdateWithQueryInstrumentation(ElasticApmTracer tracer) { - super(tracer, + super( named("executeUpdate").or(named("executeLargeUpdate")) .and(takesArgument(0, String.class)) .and(isPublic()) @@ -158,23 +151,18 @@ public ExecuteUpdateWithQueryInstrumentation(ElasticApmTracer tracer) { @Nullable @VisibleForAdvice - @Advice.OnMethodEnter(suppress = Throwable.class) + @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) public static Span onBeforeExecute(@Advice.This Statement statement, @Advice.Argument(0) String sql) { - if (tracer == null || jdbcHelperManager == null) { + if (tracer == null) { return null; } - JdbcHelper helperImpl = jdbcHelperManager.getForClassLoaderOfClass(Statement.class); - if (helperImpl == null) { - return null; - } - - return helperImpl.createJdbcSpan(sql, statement, tracer.getActive(), false); + return jdbcHelper.createJdbcSpan(sql, statement, tracer.getActive(), false); } @VisibleForAdvice - @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) + @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class, inline = false) public static void onAfterExecute(@Advice.Enter @Nullable Span span, @Advice.Thrown @Nullable Throwable t, @Advice.Return long returnValue /* bytebuddy converts int to long for us here ! */) { @@ -200,23 +188,16 @@ public static void onAfterExecute(@Advice.Enter @Nullable Span span, public static class AddBatchInstrumentation extends StatementInstrumentation { public AddBatchInstrumentation(ElasticApmTracer tracer) { - super(tracer, + super( nameStartsWith("addBatch") .and(takesArgument(0, String.class)) .and(isPublic()) ); } - @Advice.OnMethodEnter(suppress = Throwable.class) + @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) public static void storeSql(@Advice.This Statement statement, @Advice.Argument(0) String sql) { - if (jdbcHelperManager == null) { - return; - } - - JdbcHelper helperImpl = jdbcHelperManager.getForClassLoaderOfClass(Statement.class); - if (helperImpl != null) { - helperImpl.mapStatementToSql(statement, sql); - } + jdbcHelper.mapStatementToSql(statement, sql); } } @@ -231,7 +212,7 @@ public static void storeSql(@Advice.This Statement statement, @Advice.Argument(0 */ public static class ExecuteBatchInstrumentation extends StatementInstrumentation { public ExecuteBatchInstrumentation(ElasticApmTracer tracer) { - super(tracer, + super( named("executeBatch").or(named("executeLargeBatch")) .and(takesArguments(0)) .and(isPublic()) @@ -240,23 +221,18 @@ public ExecuteBatchInstrumentation(ElasticApmTracer tracer) { } @Nullable - @Advice.OnMethodEnter(suppress = Throwable.class) + @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) @SuppressWarnings("DuplicatedCode") public static Span onBeforeExecute(@Advice.This Statement statement) { - if (tracer == null || jdbcHelperManager == null) { - return null; - } - JdbcHelper helper = jdbcHelperManager.getForClassLoaderOfClass(Statement.class); - if (helper == null) { + if (tracer == null) { return null; } - - String sql = helper.retrieveSqlForStatement(statement); - return helper.createJdbcSpan(sql, statement, tracer.getActive(), true); + String sql = jdbcHelper.retrieveSqlForStatement(statement); + return jdbcHelper.createJdbcSpan(sql, statement, tracer.getActive(), true); } - @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) + @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class, inline = false) public static void onAfterExecute(@Advice.Enter @Nullable Span span, @Advice.Thrown Throwable t, @Advice.Return Object returnValue) { @@ -300,7 +276,7 @@ public static void onAfterExecute(@Advice.Enter @Nullable Span span, */ public static class ExecuteUpdateNoQueryInstrumentation extends StatementInstrumentation { public ExecuteUpdateNoQueryInstrumentation(ElasticApmTracer tracer) { - super(tracer, + super( named("executeUpdate").or(named("executeLargeUpdate")) .and(takesArguments(0)) .and(isPublic()) @@ -308,23 +284,18 @@ public ExecuteUpdateNoQueryInstrumentation(ElasticApmTracer tracer) { } @Nullable - @Advice.OnMethodEnter(suppress = Throwable.class) + @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) @SuppressWarnings("DuplicatedCode") public static Span onBeforeExecute(@Advice.This Statement statement) { - if (tracer == null || jdbcHelperManager == null) { + if (tracer == null) { return null; } - JdbcHelper helperImpl = jdbcHelperManager.getForClassLoaderOfClass(Statement.class); - if (helperImpl == null) { - return null; - } - - String sql = helperImpl.retrieveSqlForStatement(statement); - return helperImpl.createJdbcSpan(sql, statement, tracer.getActive(), true); + String sql = jdbcHelper.retrieveSqlForStatement(statement); + return jdbcHelper.createJdbcSpan(sql, statement, tracer.getActive(), true); } - @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) + @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class, inline = false) public static void onAfterExecute(@Advice.Enter @Nullable Span span, @Advice.Thrown @Nullable Throwable t, @Advice.Return long returnValue /* bytebuddy converts int to long for us here ! */) { @@ -353,7 +324,7 @@ public static void onAfterExecute(@Advice.Enter @Nullable Span span, */ public static class ExecutePreparedStatementInstrumentation extends StatementInstrumentation { public ExecutePreparedStatementInstrumentation(ElasticApmTracer tracer) { - super(tracer, + super( named("execute").or(named("executeQuery")) .and(takesArguments(0)) .and(isPublic()) @@ -361,20 +332,17 @@ public ExecutePreparedStatementInstrumentation(ElasticApmTracer tracer) { } @Nullable - @Advice.OnMethodEnter(suppress = Throwable.class) + @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) @SuppressWarnings("DuplicatedCode") public static Span onBeforeExecute(@Advice.This Statement statement) { - if (tracer != null && jdbcHelperManager != null) { - JdbcHelper helperImpl = jdbcHelperManager.getForClassLoaderOfClass(Statement.class); - if (helperImpl != null) { - @Nullable String sql = helperImpl.retrieveSqlForStatement(statement); - return helperImpl.createJdbcSpan(sql, statement, tracer.getActive(), true); - } + if (tracer != null) { + @Nullable String sql = jdbcHelper.retrieveSqlForStatement(statement); + return jdbcHelper.createJdbcSpan(sql, statement, tracer.getActive(), true); } return null; } - @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) + @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class, inline = false) public static void onAfterExecute(@Advice.This Statement statement, @Advice.Enter @Nullable Span span, @Advice.Thrown @Nullable Throwable t) { diff --git a/apm-agent-plugins/apm-jdbc-plugin/src/main/java/co/elastic/apm/agent/jdbc/helper/JdbcGlobalState.java b/apm-agent-plugins/apm-jdbc-plugin/src/main/java/co/elastic/apm/agent/jdbc/helper/JdbcGlobalState.java new file mode 100644 index 0000000000..92993a2ec9 --- /dev/null +++ b/apm-agent-plugins/apm-jdbc-plugin/src/main/java/co/elastic/apm/agent/jdbc/helper/JdbcGlobalState.java @@ -0,0 +1,52 @@ +/*- + * #%L + * Elastic APM Java agent + * %% + * Copyright (C) 2018 - 2020 Elastic and contributors + * %% + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * #L% + */ +package co.elastic.apm.agent.jdbc.helper; + +import co.elastic.apm.agent.bci.GlobalState; +import co.elastic.apm.agent.util.DataStructures; +import com.blogspot.mydailyjava.weaklockfree.WeakConcurrentMap; + +import java.sql.Connection; + +@GlobalState +public class JdbcGlobalState { + + // Important implementation note: + // + // because this class is potentially loaded from multiple classloaders, making those fields 'static' will not + // have the expected behavior, thus, any direct reference to `JdbcHelperImpl` should only be obtained from the + // HelperClassManager instance. + public static final WeakConcurrentMap statementSqlMap = DataStructures.createWeakConcurrentMapWithCleanerThread(); + public static final WeakConcurrentMap metaDataMap = DataStructures.createWeakConcurrentMapWithCleanerThread(); + public static final WeakConcurrentMap, Boolean> metadataSupported = new WeakConcurrentMap.WithInlinedExpunction, Boolean>(); + public static final WeakConcurrentMap, Boolean> connectionSupported = new WeakConcurrentMap.WithInlinedExpunction, Boolean>(); + + public static void clearInternalStorage() { + metaDataMap.clear(); + metadataSupported.clear(); + connectionSupported.clear(); + } + +} diff --git a/apm-agent-plugins/apm-jdbc-plugin/src/main/java/co/elastic/apm/agent/jdbc/helper/JdbcHelper.java b/apm-agent-plugins/apm-jdbc-plugin/src/main/java/co/elastic/apm/agent/jdbc/helper/JdbcHelper.java deleted file mode 100644 index 3ee4287e1f..0000000000 --- a/apm-agent-plugins/apm-jdbc-plugin/src/main/java/co/elastic/apm/agent/jdbc/helper/JdbcHelper.java +++ /dev/null @@ -1,72 +0,0 @@ -/*- - * #%L - * Elastic APM Java agent - * %% - * Copyright (C) 2018 - 2020 Elastic and contributors - * %% - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * #L% - */ -package co.elastic.apm.agent.jdbc.helper; - -import co.elastic.apm.agent.impl.transaction.AbstractSpan; -import co.elastic.apm.agent.impl.transaction.Span; -import co.elastic.apm.agent.util.DataStructures; -import com.blogspot.mydailyjava.weaklockfree.WeakConcurrentMap; - -import javax.annotation.Nullable; - -public abstract class JdbcHelper { - - private static final WeakConcurrentMap statementSqlMap = DataStructures.createWeakConcurrentMapWithCleanerThread(); - - public static final String DB_SPAN_TYPE = "db"; - public static final String DB_SPAN_ACTION = "query"; - - /** - * Maps the provided sql to the provided Statement object - * - * @param statement javax.sql.Statement object - * @param sql query string - */ - public void mapStatementToSql(Object statement, String sql) { - statementSqlMap.putIfAbsent(statement, sql); - } - - /** - * Returns the SQL statement belonging to provided Statement. - *

- * Might return {@code null} when the provided Statement is a wrapper of the actual statement. - *

- * - * @return the SQL statement belonging to provided Statement, or {@code null} - */ - @Nullable - public String retrieveSqlForStatement(Object statement) { - return statementSqlMap.get(statement); - } - - /** - * Clears internal data storage, should only be used for testing - */ - public abstract void clearInternalStorage(); - - @Nullable - public abstract Span createJdbcSpan(@Nullable String sql, Object statement, @Nullable AbstractSpan parent, boolean preparedStatement); - -} diff --git a/apm-agent-plugins/apm-jdbc-plugin/src/main/java/co/elastic/apm/agent/jdbc/helper/JdbcHelperImpl.java b/apm-agent-plugins/apm-jdbc-plugin/src/main/java/co/elastic/apm/agent/jdbc/helper/JdbcHelperImpl.java index 36043436f2..8ed93bb63c 100644 --- a/apm-agent-plugins/apm-jdbc-plugin/src/main/java/co/elastic/apm/agent/jdbc/helper/JdbcHelperImpl.java +++ b/apm-agent-plugins/apm-jdbc-plugin/src/main/java/co/elastic/apm/agent/jdbc/helper/JdbcHelperImpl.java @@ -24,12 +24,11 @@ */ package co.elastic.apm.agent.jdbc.helper; -import co.elastic.apm.agent.bci.VisibleForAdvice; import co.elastic.apm.agent.impl.context.Destination; import co.elastic.apm.agent.impl.transaction.AbstractSpan; import co.elastic.apm.agent.impl.transaction.Span; import co.elastic.apm.agent.jdbc.signature.SignatureParser; -import co.elastic.apm.agent.util.DataStructures; +import com.blogspot.mydailyjava.weaklockfree.DetachedThreadLocal; import com.blogspot.mydailyjava.weaklockfree.WeakConcurrentMap; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -40,36 +39,48 @@ import java.sql.SQLException; import java.sql.Statement; -@SuppressWarnings("unused") // indirect access to this class provided through HelperClassManager -public class JdbcHelperImpl extends JdbcHelper { +import static co.elastic.apm.agent.jdbc.helper.JdbcGlobalState.connectionSupported; +import static co.elastic.apm.agent.jdbc.helper.JdbcGlobalState.metaDataMap; +import static co.elastic.apm.agent.jdbc.helper.JdbcGlobalState.metadataSupported; +import static co.elastic.apm.agent.jdbc.helper.JdbcGlobalState.statementSqlMap; + +public class JdbcHelperImpl { private static final Logger logger = LoggerFactory.getLogger(JdbcHelperImpl.class); + public static final String DB_SPAN_TYPE = "db"; + public static final String DB_SPAN_ACTION = "query"; - // Important implementation note: - // - // because this class is potentially loaded from multiple classloaders, making those fields 'static' will not - // have the expected behavior, thus, any direct reference to `JdbcHelperImpl` should only be obtained from the - // HelperClassManager instance. - private final WeakConcurrentMap metaDataMap = DataStructures.createWeakConcurrentMapWithCleanerThread(); - private final WeakConcurrentMap, Boolean> metadataSupported = new WeakConcurrentMap.WithInlinedExpunction, Boolean>(); - private final WeakConcurrentMap, Boolean> connectionSupported = new WeakConcurrentMap.WithInlinedExpunction, Boolean>(); - - @VisibleForAdvice - public final ThreadLocal SIGNATURE_PARSER_THREAD_LOCAL = new ThreadLocal() { + private final DetachedThreadLocal SIGNATURE_PARSER_THREAD_LOCAL = new DetachedThreadLocal(DetachedThreadLocal.Cleaner.INLINE) { @Override - protected SignatureParser initialValue() { + protected SignatureParser initialValue(Thread thread) { return new SignatureParser(); } }; - @Override - public void clearInternalStorage() { - metaDataMap.clear(); - metadataSupported.clear(); - connectionSupported.clear(); + /** + * Maps the provided sql to the provided Statement object + * + * @param statement javax.sql.Statement object + * @param sql query string + */ + public void mapStatementToSql(Object statement, String sql) { + statementSqlMap.putIfAbsent(statement, sql); } - @Override + /** + * Returns the SQL statement belonging to provided Statement. + *

+ * Might return {@code null} when the provided Statement is a wrapper of the actual statement. + *

+ * + * @return the SQL statement belonging to provided Statement, or {@code null} + */ + @Nullable + public String retrieveSqlForStatement(Object statement) { + return statementSqlMap.get(statement); + } + + @Nullable public Span createJdbcSpan(@Nullable String sql, Object statement, @Nullable AbstractSpan parent, boolean preparedStatement) { if (!(statement instanceof Statement) || sql == null || isAlreadyMonitored(parent) || parent == null) { diff --git a/apm-agent-plugins/apm-jdbc-plugin/src/test/java/co/elastic/apm/agent/jdbc/AbstractJdbcInstrumentationTest.java b/apm-agent-plugins/apm-jdbc-plugin/src/test/java/co/elastic/apm/agent/jdbc/AbstractJdbcInstrumentationTest.java index c8a92a44b9..11ec3e65be 100644 --- a/apm-agent-plugins/apm-jdbc-plugin/src/test/java/co/elastic/apm/agent/jdbc/AbstractJdbcInstrumentationTest.java +++ b/apm-agent-plugins/apm-jdbc-plugin/src/test/java/co/elastic/apm/agent/jdbc/AbstractJdbcInstrumentationTest.java @@ -29,7 +29,7 @@ import co.elastic.apm.agent.impl.context.Destination; import co.elastic.apm.agent.impl.transaction.Span; import co.elastic.apm.agent.impl.transaction.Transaction; -import co.elastic.apm.agent.jdbc.helper.JdbcHelper; +import co.elastic.apm.agent.jdbc.helper.JdbcGlobalState; import co.elastic.apm.agent.jdbc.signature.SignatureParser; import org.junit.After; import org.junit.Before; @@ -48,8 +48,8 @@ import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; -import static co.elastic.apm.agent.jdbc.helper.JdbcHelper.DB_SPAN_ACTION; -import static co.elastic.apm.agent.jdbc.helper.JdbcHelper.DB_SPAN_TYPE; +import static co.elastic.apm.agent.jdbc.helper.JdbcHelperImpl.DB_SPAN_ACTION; +import static co.elastic.apm.agent.jdbc.helper.JdbcHelperImpl.DB_SPAN_TYPE; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.assertj.core.api.Assertions.fail; @@ -161,12 +161,7 @@ private void executeTest(JdbcTask task) throws SQLException { // clear internal jdbc helper required due to metadata caching and global state about unsupported // JDBC driver features (based on classes instances) - if (JdbcInstrumentation.jdbcHelperManager != null) { - JdbcHelper jdbcHelper = JdbcInstrumentation.jdbcHelperManager.getForClassLoaderOfClass(Statement.class); - if (jdbcHelper != null) { - jdbcHelper.clearInternalStorage(); - } - } + JdbcGlobalState.clearInternalStorage(); } } diff --git a/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttp3ClientInstrumentation.java b/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttp3ClientInstrumentation.java index fc624caec1..7a17017a13 100644 --- a/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttp3ClientInstrumentation.java +++ b/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttp3ClientInstrumentation.java @@ -31,7 +31,7 @@ import co.elastic.apm.agent.impl.transaction.AbstractSpan; import co.elastic.apm.agent.impl.transaction.Span; import co.elastic.apm.agent.impl.transaction.TextHeaderSetter; -import co.elastic.apm.agent.util.RemoveOnGetThreadLocal; +import co.elastic.apm.agent.threadlocal.RemoveOnGetThreadLocal; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.method.MethodDescription; import net.bytebuddy.description.type.TypeDescription; diff --git a/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttpClientInstrumentation.java b/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttpClientInstrumentation.java index 7097bb7ae6..c1716f0b4a 100644 --- a/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttpClientInstrumentation.java +++ b/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttpClientInstrumentation.java @@ -31,7 +31,7 @@ import co.elastic.apm.agent.impl.transaction.AbstractSpan; import co.elastic.apm.agent.impl.transaction.Span; import co.elastic.apm.agent.impl.transaction.TextHeaderSetter; -import co.elastic.apm.agent.util.RemoveOnGetThreadLocal; +import co.elastic.apm.agent.threadlocal.RemoveOnGetThreadLocal; import com.squareup.okhttp.HttpUrl; import com.squareup.okhttp.Request; import net.bytebuddy.asm.Advice; diff --git a/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/AbstractServletInstrumentation.java b/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/AbstractServletInstrumentation.java index 01deca5a0c..29df56ff53 100644 --- a/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/AbstractServletInstrumentation.java +++ b/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/AbstractServletInstrumentation.java @@ -41,10 +41,15 @@ public Collection getInstrumentationGroupNames() { } @Override - public final ElementMatcher.Junction getClassLoaderMatcher() { + public ElementMatcher.Junction getClassLoaderMatcher() { // this class has been introduced in servlet spec 3.0 // choice of class name to use for this test does not work as expected across all application servers // for example, 'javax.servlet.annotation.WebServlet' annotation is not working as expected on Payara return CustomElementMatchers.classLoaderCanLoadClass("javax.servlet.AsyncContext"); } + + @Override + public boolean indyDispatch() { + return true; + } } diff --git a/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/AsyncInstrumentation.java b/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/AsyncInstrumentation.java index 1e717c74ca..17d2346c91 100644 --- a/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/AsyncInstrumentation.java +++ b/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/AsyncInstrumentation.java @@ -24,11 +24,10 @@ */ package co.elastic.apm.agent.servlet; -import co.elastic.apm.agent.bci.HelperClassManager; import co.elastic.apm.agent.bci.VisibleForAdvice; import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToArgument; -import co.elastic.apm.agent.impl.ElasticApmTracer; import co.elastic.apm.agent.impl.transaction.Transaction; +import co.elastic.apm.agent.servlet.helper.AsyncContextAdviceHelperImpl; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.NamedElement; import net.bytebuddy.description.method.MethodDescription; @@ -65,22 +64,6 @@ public abstract class AsyncInstrumentation extends AbstractServletInstrumentation { private static final String SERVLET_API_ASYNC_GROUP_NAME = "servlet-api-async"; - @Nullable - @VisibleForAdvice - // referring to AsyncContext is legal because of type erasure - public static HelperClassManager> asyncHelperManager; - - public AsyncInstrumentation(ElasticApmTracer tracer) { - synchronized (AsyncInstrumentation.class) { - if (asyncHelperManager == null) { - asyncHelperManager = HelperClassManager.ForSingleClassLoader.of(tracer, - "co.elastic.apm.agent.servlet.helper.AsyncContextAdviceHelperImpl", - "co.elastic.apm.agent.servlet.helper.AsyncContextAdviceHelperImpl$ApmAsyncListenerAllocator", - "co.elastic.apm.agent.servlet.helper.ApmAsyncListener"); - } - - } - } @Override public Collection getInstrumentationGroupNames() { @@ -92,9 +75,6 @@ public interface AsyncContextAdviceHelper { } public static class StartAsyncInstrumentation extends AsyncInstrumentation { - public StartAsyncInstrumentation(ElasticApmTracer tracer) { - super(tracer); - } @Override public ElementMatcher getTypeMatcherPreFilter() { @@ -136,23 +116,17 @@ public Class getAdviceClass() { @VisibleForAdvice public static class StartAsyncAdvice { + @VisibleForAdvice + public static AsyncContextAdviceHelper asyncHelper = new AsyncContextAdviceHelperImpl(tracer); - @Advice.OnMethodExit(suppress = Throwable.class) - private static void onExitStartAsync(@Advice.Return AsyncContext asyncContext) { - if (tracer != null && asyncHelperManager != null) { - AsyncContextAdviceHelper helperImpl = asyncHelperManager.getForClassLoaderOfClass(AsyncContext.class); - if (helperImpl != null) { - helperImpl.onExitStartAsync(asyncContext); - } - } + @Advice.OnMethodExit(suppress = Throwable.class, inline = false) + public static void onExitStartAsync(@Advice.Return AsyncContext asyncContext) { + asyncHelper.onExitStartAsync(asyncContext); } } } public static class AsyncContextInstrumentation extends AsyncInstrumentation { - public AsyncContextInstrumentation(ElasticApmTracer tracer) { - super(tracer); - } @Override public ElementMatcher getTypeMatcherPreFilter() { diff --git a/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/FilterChainInstrumentation.java b/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/FilterChainInstrumentation.java index 46afd92cef..f61ae3e449 100644 --- a/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/FilterChainInstrumentation.java +++ b/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/FilterChainInstrumentation.java @@ -24,7 +24,6 @@ */ package co.elastic.apm.agent.servlet; -import co.elastic.apm.agent.impl.ElasticApmTracer; import net.bytebuddy.description.NamedElement; import net.bytebuddy.description.method.MethodDescription; import net.bytebuddy.description.type.TypeDescription; @@ -42,10 +41,6 @@ */ public class FilterChainInstrumentation extends AbstractServletInstrumentation { - public FilterChainInstrumentation(ElasticApmTracer tracer) { - ServletApiAdvice.init(tracer); - } - @Override public ElementMatcher getTypeMatcherPreFilter() { return nameContains("Chain"); diff --git a/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/RequestStreamRecordingInstrumentation.java b/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/RequestStreamRecordingInstrumentation.java index f4489736af..537764f739 100644 --- a/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/RequestStreamRecordingInstrumentation.java +++ b/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/RequestStreamRecordingInstrumentation.java @@ -24,19 +24,16 @@ */ package co.elastic.apm.agent.servlet; -import co.elastic.apm.agent.bci.HelperClassManager; import co.elastic.apm.agent.bci.VisibleForAdvice; import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToReturn; -import co.elastic.apm.agent.impl.ElasticApmTracer; -import co.elastic.apm.agent.impl.context.Request; import co.elastic.apm.agent.impl.transaction.Transaction; +import co.elastic.apm.agent.servlet.helper.RecordingServletInputStreamWrapper; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.NamedElement; import net.bytebuddy.description.method.MethodDescription; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.matcher.ElementMatcher; -import javax.annotation.Nullable; import javax.servlet.ServletInputStream; import java.util.Arrays; import java.util.Collection; @@ -51,22 +48,6 @@ public class RequestStreamRecordingInstrumentation extends AbstractServletInstrumentation { - @Nullable - @VisibleForAdvice - // referring to InputStreamWrapperFactory is legal because of type erasure - public static HelperClassManager wrapperHelperClassManager; - - public RequestStreamRecordingInstrumentation(ElasticApmTracer tracer) { - ServletApiAdvice.init(tracer); - synchronized (RequestStreamRecordingInstrumentation.class) { - if (wrapperHelperClassManager == null) { - wrapperHelperClassManager = HelperClassManager.ForSingleClassLoader.of(tracer, - "co.elastic.apm.agent.servlet.helper.InputStreamFactoryHelperImpl", - "co.elastic.apm.agent.servlet.helper.RecordingServletInputStreamWrapper"); - } - } - } - @Override public ElementMatcher getTypeMatcherPreFilter() { return nameContains("Request"); @@ -92,10 +73,6 @@ public Class getAdviceClass() { return GetInputStreamAdvice.class; } - public interface InputStreamWrapperFactory { - ServletInputStream wrap(Request request, ServletInputStream servletInputStream); - } - public static class GetInputStreamAdvice { @VisibleForAdvice @@ -117,14 +94,14 @@ public static boolean onReadEnter(@Advice.This Object thiz) { @Advice.OnMethodExit(suppress = Throwable.class, inline = false) public static ServletInputStream afterGetInputStream(@Advice.Return ServletInputStream inputStream, @Advice.Enter boolean nested) { - if (nested == Boolean.TRUE || tracer == null || wrapperHelperClassManager == null) { + if (nested == Boolean.TRUE || tracer == null) { return inputStream; } try { final Transaction transaction = tracer.currentTransaction(); // only wrap if the body buffer has been initialized via ServletTransactionHelper.startCaptureBody if (transaction != null && transaction.getContext().getRequest().getBodyBuffer() != null) { - return wrapperHelperClassManager.getForClassLoaderOfClass(inputStream.getClass()).wrap(transaction.getContext().getRequest(), inputStream); + return new RecordingServletInputStreamWrapper(transaction.getContext().getRequest(), inputStream); } else { return inputStream; } diff --git a/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/ServletApiAdvice.java b/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/ServletApiAdvice.java index cc145d521a..8d8733f835 100644 --- a/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/ServletApiAdvice.java +++ b/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/ServletApiAdvice.java @@ -24,13 +24,14 @@ */ package co.elastic.apm.agent.servlet; -import co.elastic.apm.agent.bci.VisibleForAdvice; import co.elastic.apm.agent.configuration.CoreConfiguration; -import co.elastic.apm.agent.impl.ElasticApmTracer; import co.elastic.apm.agent.impl.Scope; import co.elastic.apm.agent.impl.context.Request; import co.elastic.apm.agent.impl.context.Response; import co.elastic.apm.agent.impl.transaction.Transaction; +import co.elastic.apm.agent.servlet.helper.ServletTransactionCreationHelper; +import co.elastic.apm.agent.threadlocal.RemoveOnGetThreadLocal; +import co.elastic.apm.agent.threadlocal.ThreadLocalRegistry; import net.bytebuddy.asm.Advice; import javax.annotation.Nullable; @@ -48,8 +49,10 @@ import java.util.List; import java.util.Map; +import static co.elastic.apm.agent.bci.ElasticApmInstrumentation.tracer; import static co.elastic.apm.agent.servlet.ServletTransactionHelper.TRANSACTION_ATTRIBUTE; import static co.elastic.apm.agent.servlet.ServletTransactionHelper.determineServiceName; +import static java.lang.Boolean.FALSE; /** * Only the methods annotated with {@link Advice.OnMethodEnter} and {@link Advice.OnMethodExit} may contain references to @@ -59,44 +62,30 @@ */ public class ServletApiAdvice { - @Nullable - @VisibleForAdvice - public static ServletTransactionHelper servletTransactionHelper; - - @Nullable - @VisibleForAdvice - public static ElasticApmTracer tracer; - - @VisibleForAdvice - public static ThreadLocal excluded = new ThreadLocal() { - @Override - protected Boolean initialValue() { - return Boolean.FALSE; - } - }; - - @VisibleForAdvice - public static final List requestExceptionAttributes = Arrays.asList("javax.servlet.error.exception", "exception", "org.springframework.web.servlet.DispatcherServlet.EXCEPTION", "co.elastic.apm.exception"); + private static final ServletTransactionHelper servletTransactionHelper; + private static final ServletTransactionCreationHelper servletTransactionCreationHelper; - static void init(ElasticApmTracer tracer) { - ServletApiAdvice.tracer = tracer; + static { servletTransactionHelper = new ServletTransactionHelper(tracer); + servletTransactionCreationHelper = new ServletTransactionCreationHelper(tracer); } - @Advice.OnMethodEnter(suppress = Throwable.class) - public static void onEnterServletService(@Advice.Argument(0) ServletRequest servletRequest, - @Advice.Local("transaction") Transaction transaction, - @Advice.Local("scope") Scope scope) { + private static final RemoveOnGetThreadLocal excluded = ThreadLocalRegistry.get(ServletApiAdvice.class, "excluded", false); + private static final List requestExceptionAttributes = Arrays.asList("javax.servlet.error.exception", "exception", "org.springframework.web.servlet.DispatcherServlet.EXCEPTION", "co.elastic.apm.exception"); + + @Nullable + @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) + public static Object onEnterServletService(@Advice.Argument(0) ServletRequest servletRequest) { if (tracer == null) { - return; + return null; } + Transaction transaction = null; // re-activate transactions for async requests final Transaction transactionAttr = (Transaction) servletRequest.getAttribute(TRANSACTION_ATTRIBUTE); if (tracer.currentTransaction() == null && transactionAttr != null) { - scope = transactionAttr.activateInScope(); + return transactionAttr.activateInScope(); } if (tracer.isRunning() && - servletTransactionHelper != null && servletRequest instanceof HttpServletRequest && servletRequest.getDispatcherType() == DispatcherType.REQUEST && !Boolean.TRUE.equals(excluded.get())) { @@ -108,18 +97,12 @@ public static void onEnterServletService(@Advice.Argument(0) ServletRequest serv } final HttpServletRequest request = (HttpServletRequest) servletRequest; - if (ServletInstrumentation.servletTransactionCreationHelperManager != null) { - ServletInstrumentation.ServletTransactionCreationHelper helper = - ServletInstrumentation.servletTransactionCreationHelperManager.getForClassLoaderOfClass(HttpServletRequest.class); - if (helper != null) { - transaction = helper.createAndActivateTransaction(request); - } - } + transaction = servletTransactionCreationHelper.createAndActivateTransaction(request); if (transaction == null) { // if the request is excluded, avoid matching all exclude patterns again on each filter invocation excluded.set(Boolean.TRUE); - return; + return null; } final Request req = transaction.getContext().getRequest(); if (transaction.isSampled() && tracer.getConfig(CoreConfiguration.class).isCaptureHeaders()) { @@ -141,19 +124,27 @@ public static void onEnterServletService(@Advice.Argument(0) ServletRequest serv request.getScheme(), request.getServerName(), request.getServerPort(), request.getRequestURI(), request.getQueryString(), request.getRemoteAddr(), request.getHeader("Content-Type")); } + return transaction; } - @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) + @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class, inline = false) public static void onExitServletService(@Advice.Argument(0) ServletRequest servletRequest, @Advice.Argument(1) ServletResponse servletResponse, - @Advice.Local("transaction") @Nullable Transaction transaction, - @Advice.Local("scope") @Nullable Scope scope, + @Advice.Enter @Nullable Object transactionOrScope, @Advice.Thrown @Nullable Throwable t, @Advice.This Object thiz) { if (tracer == null) { return; } - excluded.set(Boolean.FALSE); + Transaction transaction = null; + Scope scope = null; + if (transactionOrScope instanceof Transaction) { + transaction = (Transaction) transactionOrScope; + } else if (transactionOrScope instanceof Scope) { + scope = (Scope) transactionOrScope; + } + + excluded.set(FALSE); if (scope != null) { scope.close(); } @@ -166,8 +157,7 @@ public static void onExitServletService(@Advice.Argument(0) ServletRequest servl ServletTransactionHelper.setUsernameIfUnset(userPrincipal != null ? userPrincipal.getName() : null, currentTransaction.getContext()); } } - if (servletTransactionHelper != null && - transaction != null && + if (transaction != null && servletRequest instanceof HttpServletRequest && servletResponse instanceof HttpServletResponse) { diff --git a/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/helper/InputStreamFactoryHelperImpl.java b/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/ServletGlobalState.java similarity index 63% rename from apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/helper/InputStreamFactoryHelperImpl.java rename to apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/ServletGlobalState.java index 843a6fb126..2b0b60dd72 100644 --- a/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/helper/InputStreamFactoryHelperImpl.java +++ b/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/ServletGlobalState.java @@ -11,9 +11,9 @@ * the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -22,16 +22,21 @@ * under the License. * #L% */ -package co.elastic.apm.agent.servlet.helper; +package co.elastic.apm.agent.servlet; -import co.elastic.apm.agent.impl.context.Request; -import co.elastic.apm.agent.servlet.RequestStreamRecordingInstrumentation; +import co.elastic.apm.agent.bci.GlobalState; -import javax.servlet.ServletInputStream; +import java.util.Collections; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; -public class InputStreamFactoryHelperImpl implements RequestStreamRecordingInstrumentation.InputStreamWrapperFactory { - @Override - public ServletInputStream wrap(Request request, ServletInputStream servletInputStream) { - return new RecordingServletInputStreamWrapper(request, servletInputStream); +@GlobalState +public class ServletGlobalState { + + public static final Set nameInitialized = Collections.newSetFromMap(new ConcurrentHashMap()); + + // visible for testing as clearing cache is required between tests execution + static void clearServiceNameCache() { + nameInitialized.clear(); } } diff --git a/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/ServletInstrumentation.java b/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/ServletInstrumentation.java index 960beb15f6..6af55ad296 100644 --- a/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/ServletInstrumentation.java +++ b/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/ServletInstrumentation.java @@ -24,18 +24,11 @@ */ package co.elastic.apm.agent.servlet; -import co.elastic.apm.agent.bci.HelperClassManager; -import co.elastic.apm.agent.bci.VisibleForAdvice; -import co.elastic.apm.agent.impl.ElasticApmTracer; -import co.elastic.apm.agent.impl.transaction.Transaction; import net.bytebuddy.description.NamedElement; import net.bytebuddy.description.method.MethodDescription; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.matcher.ElementMatcher; -import javax.annotation.Nullable; -import javax.servlet.http.HttpServletRequest; - import static net.bytebuddy.matcher.ElementMatchers.hasSuperType; import static net.bytebuddy.matcher.ElementMatchers.isInterface; import static net.bytebuddy.matcher.ElementMatchers.nameContains; @@ -57,21 +50,6 @@ public class ServletInstrumentation extends AbstractServletInstrumentation { static final String SERVLET_API = "servlet-api"; - @Nullable - @VisibleForAdvice - // referring to HttpServletRequest is legal because of type erasure - public static HelperClassManager> servletTransactionCreationHelperManager; - - public ServletInstrumentation(ElasticApmTracer tracer) { - ServletApiAdvice.init(tracer); - if (servletTransactionCreationHelperManager == null) { - servletTransactionCreationHelperManager = HelperClassManager.ForSingleClassLoader.of(tracer, - "co.elastic.apm.agent.servlet.helper.ServletTransactionCreationHelperImpl", - "co.elastic.apm.agent.servlet.helper.ServletRequestHeaderGetter" - ); - } - } - @Override public ElementMatcher getTypeMatcherPreFilter() { return nameContains("Servlet").or(nameContainsIgnoreCase("jsp")); @@ -95,9 +73,4 @@ public Class getAdviceClass() { return ServletApiAdvice.class; } - @VisibleForAdvice - public interface ServletTransactionCreationHelper { - @Nullable - Transaction createAndActivateTransaction(R request); - } } diff --git a/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/ServletTransactionHelper.java b/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/ServletTransactionHelper.java index 0bbb1849f8..28fe34c676 100644 --- a/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/ServletTransactionHelper.java +++ b/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/ServletTransactionHelper.java @@ -41,15 +41,14 @@ import javax.annotation.Nullable; import java.util.Arrays; -import java.util.Collections; import java.util.HashSet; import java.util.Map; import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; import static co.elastic.apm.agent.configuration.CoreConfiguration.EventType.OFF; import static co.elastic.apm.agent.impl.transaction.AbstractSpan.PRIO_DEFAULT; import static co.elastic.apm.agent.impl.transaction.AbstractSpan.PRIO_LOW_LEVEL_FRAMEWORK; +import static co.elastic.apm.agent.servlet.ServletGlobalState.nameInitialized; /** * This class must not import classes from {@code javax.servlet} due to class loader issues. @@ -66,7 +65,6 @@ public class ServletTransactionHelper { private static final String CONTENT_TYPE_FROM_URLENCODED = "application/x-www-form-urlencoded"; private static final WildcardMatcher ENDS_WITH_JSP = WildcardMatcher.valueOf("*.jsp"); - private static final Set nameInitialized = Collections.newSetFromMap(new ConcurrentHashMap()); private final Logger logger = LoggerFactory.getLogger(ServletTransactionHelper.class); @@ -80,11 +78,6 @@ public ServletTransactionHelper(ElasticApmTracer tracer) { this.webConfiguration = tracer.getConfig(WebConfiguration.class); } - // visible for testing as clearing cache is required between tests execution - static void clearServiceNameCache() { - nameInitialized.clear(); - } - @VisibleForAdvice public static void determineServiceName(@Nullable String servletContextName, ClassLoader servletContextClassLoader, @Nullable String contextPath) { if (ElasticApmInstrumentation.tracer == null || !nameInitialized.add(contextPath == null ? "null" : contextPath)) { diff --git a/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/ServletVersionInstrumentation.java b/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/ServletVersionInstrumentation.java index 11e920502c..8ae61bbdfa 100644 --- a/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/ServletVersionInstrumentation.java +++ b/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/ServletVersionInstrumentation.java @@ -24,7 +24,6 @@ */ package co.elastic.apm.agent.servlet; -import co.elastic.apm.agent.bci.ElasticApmInstrumentation; import co.elastic.apm.agent.bci.VisibleForAdvice; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.NamedElement; @@ -40,10 +39,8 @@ import javax.servlet.ServletContext; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; -import java.util.Collection; -import java.util.Collections; -import static co.elastic.apm.agent.servlet.ServletInstrumentation.SERVLET_API; +import static net.bytebuddy.matcher.ElementMatchers.any; import static net.bytebuddy.matcher.ElementMatchers.hasSuperType; import static net.bytebuddy.matcher.ElementMatchers.isInterface; import static net.bytebuddy.matcher.ElementMatchers.nameContains; @@ -58,7 +55,7 @@ * Does not inherit from {@link AbstractServletInstrumentation} in order to still instrument when servlet version is not * supported. */ -public abstract class ServletVersionInstrumentation extends ElasticApmInstrumentation { +public abstract class ServletVersionInstrumentation extends AbstractServletInstrumentation { @VisibleForAdvice public static final Logger logger = LoggerFactory.getLogger(ServletVersionInstrumentation.class); @@ -78,8 +75,8 @@ public ElementMatcher getTypeMatcher() { } @Override - public Collection getInstrumentationGroupNames() { - return Collections.singleton(SERVLET_API); + public ElementMatcher.Junction getClassLoaderMatcher() { + return any(); } /** @@ -93,9 +90,9 @@ public ElementMatcher getMethodMatcher() { .and(takesArgument(0, named("javax.servlet.ServletConfig"))); } - @Advice.OnMethodEnter(suppress = Throwable.class) + @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) @SuppressWarnings("Duplicates") // duplication is fine here as it allows to inline code - private static void onEnter(@Advice.Argument(0) @Nullable ServletConfig servletConfig) { + public static void onEnter(@Advice.Argument(0) @Nullable ServletConfig servletConfig) { if (alreadyLogged) { return; } @@ -132,9 +129,9 @@ public ElementMatcher getMethodMatcher() { .and(takesArgument(1, named("javax.servlet.ServletResponse"))); } - @Advice.OnMethodEnter(suppress = Throwable.class) + @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) @SuppressWarnings("Duplicates") // duplication is fine here as it allows to inline code - private static void onEnter(@Advice.This Servlet servlet) { + public static void onEnter(@Advice.This Servlet servlet) { if (alreadyLogged) { return; } diff --git a/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/helper/ServletTransactionCreationHelperImpl.java b/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/helper/ServletTransactionCreationHelper.java similarity index 91% rename from apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/helper/ServletTransactionCreationHelperImpl.java rename to apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/helper/ServletTransactionCreationHelper.java index f260be734c..f9198cc839 100644 --- a/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/helper/ServletTransactionCreationHelperImpl.java +++ b/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/helper/ServletTransactionCreationHelper.java @@ -29,7 +29,6 @@ import co.elastic.apm.agent.impl.context.web.WebConfiguration; import co.elastic.apm.agent.impl.transaction.Transaction; import co.elastic.apm.agent.matcher.WildcardMatcher; -import co.elastic.apm.agent.servlet.ServletInstrumentation; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -38,21 +37,20 @@ import java.util.Objects; @SuppressWarnings("unused") -public class ServletTransactionCreationHelperImpl implements ServletInstrumentation.ServletTransactionCreationHelper { +public class ServletTransactionCreationHelper { - private static final Logger logger = LoggerFactory.getLogger(ServletTransactionCreationHelperImpl.class); + private static final Logger logger = LoggerFactory.getLogger(ServletTransactionCreationHelper.class); private final ElasticApmTracer tracer; private final CoreConfiguration coreConfiguration; private final WebConfiguration webConfiguration; - public ServletTransactionCreationHelperImpl(ElasticApmTracer tracer) { + public ServletTransactionCreationHelper(ElasticApmTracer tracer) { this.tracer = tracer; coreConfiguration = tracer.getConfig(CoreConfiguration.class); webConfiguration = tracer.getConfig(WebConfiguration.class); } - @Override @Nullable public Transaction createAndActivateTransaction(HttpServletRequest request) { Transaction transaction = null; diff --git a/apm-agent-plugins/apm-servlet-plugin/src/test/java/co/elastic/apm/agent/servlet/AbstractServletTest.java b/apm-agent-plugins/apm-servlet-plugin/src/test/java/co/elastic/apm/agent/servlet/AbstractServletTest.java index 574587e1cd..311e10103d 100644 --- a/apm-agent-plugins/apm-servlet-plugin/src/test/java/co/elastic/apm/agent/servlet/AbstractServletTest.java +++ b/apm-agent-plugins/apm-servlet-plugin/src/test/java/co/elastic/apm/agent/servlet/AbstractServletTest.java @@ -53,7 +53,7 @@ void initServerAndClient() throws Exception { // because we reuse the same classloader with different servlet context names // we need to explicitly reset the name cache to make service name detection work as expected - ServletTransactionHelper.clearServiceNameCache(); + ServletGlobalState.clearServiceNameCache(); // server is not reused between tests as handler is provided from subclass // another alternative diff --git a/apm-agent-plugins/apm-servlet-plugin/src/test/java/co/elastic/apm/agent/servlet/AsyncServletTest.java b/apm-agent-plugins/apm-servlet-plugin/src/test/java/co/elastic/apm/agent/servlet/AsyncServletTest.java index 63d5ed3ef6..7d3463ea8e 100644 --- a/apm-agent-plugins/apm-servlet-plugin/src/test/java/co/elastic/apm/agent/servlet/AsyncServletTest.java +++ b/apm-agent-plugins/apm-servlet-plugin/src/test/java/co/elastic/apm/agent/servlet/AsyncServletTest.java @@ -195,11 +195,11 @@ public void init(FilterConfig filterConfig) { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { assertThat(tracer.currentTransaction()) - .describedAs("should be within a transaction at beginning of doFilter") + .describedAs("should be within a transaction at beginning of doFilter %s", request) .isNotNull(); chain.doFilter(request, response); assertThat(tracer.currentTransaction()) - .describedAs("should be within a transaction at end of doFilter") + .describedAs("should be within a transaction at end of doFilter %s", request) .isNotNull(); } diff --git a/apm-agent-plugins/apm-spring-webmvc-plugin/src/test/java/co/elastic/apm/agent/spring/webmvc/exception/AbstractExceptionHandlerInstrumentationTest.java b/apm-agent-plugins/apm-spring-webmvc-plugin/src/test/java/co/elastic/apm/agent/spring/webmvc/exception/AbstractExceptionHandlerInstrumentationTest.java index d050775cef..6d3f31d859 100644 --- a/apm-agent-plugins/apm-spring-webmvc-plugin/src/test/java/co/elastic/apm/agent/spring/webmvc/exception/AbstractExceptionHandlerInstrumentationTest.java +++ b/apm-agent-plugins/apm-spring-webmvc-plugin/src/test/java/co/elastic/apm/agent/spring/webmvc/exception/AbstractExceptionHandlerInstrumentationTest.java @@ -75,7 +75,7 @@ public static void setUpAll() { tracer = mockInstrumentationSetup.getTracer(); config = mockInstrumentationSetup.getConfig(); ElasticApmAgent.initInstrumentation(tracer, ByteBuddyAgent.install(), - Arrays.asList(new ServletInstrumentation(tracer), new ExceptionHandlerInstrumentation())); + Arrays.asList(new ServletInstrumentation(), new ExceptionHandlerInstrumentation())); } @AfterClass diff --git a/apm-agent-plugins/apm-spring-webmvc-plugin/src/test/java/co/elastic/apm/agent/spring/webmvc/template/AbstractViewRenderingInstrumentationTest.java b/apm-agent-plugins/apm-spring-webmvc-plugin/src/test/java/co/elastic/apm/agent/spring/webmvc/template/AbstractViewRenderingInstrumentationTest.java index dd7f56ef83..94fb6e0b2e 100644 --- a/apm-agent-plugins/apm-spring-webmvc-plugin/src/test/java/co/elastic/apm/agent/spring/webmvc/template/AbstractViewRenderingInstrumentationTest.java +++ b/apm-agent-plugins/apm-spring-webmvc-plugin/src/test/java/co/elastic/apm/agent/spring/webmvc/template/AbstractViewRenderingInstrumentationTest.java @@ -73,7 +73,7 @@ static void beforeAll() { reporter = mockInstrumentationSetup.getReporter(); config = mockInstrumentationSetup.getConfig(); tracer = mockInstrumentationSetup.getTracer(); - ElasticApmAgent.initInstrumentation(tracer, ByteBuddyAgent.install(), Arrays.asList(new ServletInstrumentation(tracer), new ViewRenderInstrumentation())); + ElasticApmAgent.initInstrumentation(tracer, ByteBuddyAgent.install(), Arrays.asList(new ServletInstrumentation(), new ViewRenderInstrumentation())); } @AfterAll diff --git a/elastic-apm-agent/pom.xml b/elastic-apm-agent/pom.xml index 9bc17f8037..fa2411c37e 100644 --- a/elastic-apm-agent/pom.xml +++ b/elastic-apm-agent/pom.xml @@ -311,6 +311,10 @@ net.bytebuddy co.elastic.apm.agent.shaded.bytebuddy + + org.objectweb.asm + co.elastic.apm.agent.shaded.asm + com.dslplatform co.elastic.apm.agent.shaded.dslplatform diff --git a/integration-tests/application-server-integration-tests/src/test/java/co/elastic/apm/servlet/WildFlyIT.java b/integration-tests/application-server-integration-tests/src/test/java/co/elastic/apm/servlet/WildFlyIT.java index 87a8a0901a..521c9b42d6 100644 --- a/integration-tests/application-server-integration-tests/src/test/java/co/elastic/apm/servlet/WildFlyIT.java +++ b/integration-tests/application-server-integration-tests/src/test/java/co/elastic/apm/servlet/WildFlyIT.java @@ -66,6 +66,8 @@ public static Iterable data() { @Override protected void enableDebugging(GenericContainer servletContainer) { servletContainer.withEnv("JAVA_OPTS", "-javaagent:/elastic-apm-agent.jar -Djava.net.preferIPv4Stack=true -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005"); + // for 16.0.0.Final + // servletContainer.withEnv("JAVA_OPTS", "-javaagent:/elastic-apm-agent.jar -Djava.net.preferIPv4Stack=true -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005"); } @Override diff --git a/pom.xml b/pom.xml index ed9d02d277..1eb4372f98 100644 --- a/pom.xml +++ b/pom.xml @@ -96,7 +96,7 @@ 5.0.15.RELEASE 9.4.11.v20180605 0.1.19 - 1.10.11-SNAPSHOT + 1.10.11 1.17 @@ -373,6 +373,7 @@ file:///${apm-agent-parent.base.dir}/licenses false true + false true Elastic APM Java agent + 1.3.176
${project.groupId} From 75b5a29922e48221739393983d067a616301c237 Mon Sep 17 00:00:00 2001 From: Felix Barnsteiner Date: Mon, 8 Jun 2020 08:42:20 +0200 Subject: [PATCH 10/35] Fix IndyBootstrapDispatcher noop MethodHandle --- .../elastic/apm/agent/bci/IndyBootstrap.java | 13 +++------ .../resources/bootstrap/IndyBootstrap.clazz | Bin 2712 -> 0 bytes .../bootstrap/IndyBootstrapDispatcher.clazz | Bin 0 -> 3401 bytes ...trap.java => IndyBootstrapDispatcher.java} | 25 +++++++++++++++--- 4 files changed, 26 insertions(+), 12 deletions(-) delete mode 100644 apm-agent-core/src/main/resources/bootstrap/IndyBootstrap.clazz create mode 100644 apm-agent-core/src/main/resources/bootstrap/IndyBootstrapDispatcher.clazz rename apm-agent-core/src/main/resources/bootstrap/{IndyBootstrap.java => IndyBootstrapDispatcher.java} (64%) diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/IndyBootstrap.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/IndyBootstrap.java index 012bf9005d..05bd304e4f 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/IndyBootstrap.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/IndyBootstrap.java @@ -46,18 +46,13 @@ /** * When {@link ElasticApmInstrumentation#indyDispatch()} returns {@code true}, * we instruct byte buddy to dispatch {@linkplain Advice.OnMethodEnter#inline()} non-inlined advices} via an invokedynamic (indy) instruction. - *

- * - *

- * - * @see ElasticApmInstrumentation#indyDispatch() */ public class IndyBootstrap { - private static final String INDY_BOOTSTRAP_CLASS_NAME = "java.lang.IndyBootstrap"; - private static final String INDY_BOOTSTRAP_RESOURCE = "bootstrap/IndyBootstrap.clazz"; + private static final String INDY_BOOTSTRAP_CLASS_NAME = "java.lang.IndyBootstrapDispatcher"; + private static final String INDY_BOOTSTRAP_RESOURCE = "bootstrap/IndyBootstrapDispatcher.clazz"; private static final ConcurrentMap> classesByPackage = new ConcurrentHashMap<>(); @Nullable - private static Method indyBootstrapMethod; + static Method indyBootstrapMethod; public static Method getIndyBootstrapMethod() { if (indyBootstrapMethod != null) { @@ -87,7 +82,7 @@ private static Class initIndyBootstrap() throws Exception { } /** - * Is called by {@code java.lang.IndyBootstrap#bootstrap} via reflection. + * Is called by {@code java.lang.IndyBootstrapDispatcher#bootstrap} via reflection. *

*

* This is to make it impossible for OSGi or other filtering class loaders to restrict access to classes in the bootstrap class loader. diff --git a/apm-agent-core/src/main/resources/bootstrap/IndyBootstrap.clazz b/apm-agent-core/src/main/resources/bootstrap/IndyBootstrap.clazz deleted file mode 100644 index d6679e595ac94d012d1618718ea28a25af704bdd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2712 zcmc&$TXz#x6#fpmoMbxCQlJp-AkYh!3MiKpQ7Du~TM(vJBHpISGz^{2WKAYj-dqd+ zi);BLK0uc)T|TKx|C9QinWS@40{Z4d&YanMe|zuqoxOMFkH3ES8Nhi21~Pc3i0!y0 zaoxZ)ZWy?U8HrmGvj!w81}@`W1Mgu@E%OF$X3Z!@2%iu;O&`nzjA9=d_L`z$wklibB_K=#n1b!sDi+D_=B^qw6L;_$08d?s%w- zYe{q<)3)bXZsd?fL56l|7B|1FInBroeCmwQiQ1vBQWMy&86~L*jHCuPoMpT1MT@rA zjyX>+?#|L9-W%Oz{U9Khhcrk1jzHSI4(+3_Hr=3EV92hoxiu##j@$O?rW`t4Y_dxZ zcHm_};wxcAC9dN$C!U8~zZHe;RmYE8)d4N~e~0YOpxVD-%~q_f{*iYb*11Gd7c0l=x8MzC@KxX1NXD=H-&7KmWI4 zR(Wc<_bUcX{om@@u!442b7tJQ^?Q2yjH*c!JF&~eFm|vQHW#VHM>};b2*!Vd5cN6OXWB!o#Y-krdu_9k8x!Kk8n( z%GDRxof`I>N8os>=|f((J$eqBu*h0_2{b9^!BiVpl)!;h(kB$hL#nT^xErdx<(irX zcb3@+V?AT2W5FWblfrrjJf}bsN8qwvpq***Kr4)nNS|{XGiR{LsT;+eun^icmivw| z-LN~!G>*G@Tnj_{vA}@ud{K$-MWIjVxg_A8lWhdfG@M9aR5$PF`r98&iRi;G|8okp zPIU;}(7h%}yqb2Y_ zI?N0;Ti3CzJn$n1tEGYG7%CI;9g{5b6KaN^O-S#_$y;;$we}t+dGzb(r}3)EptQNW1-bjW~nW1H_)-N(zV` z<5TU%=|OS&1SfHdRH@x*%G5nP!5MnV#=A*kbMfwVYV+~#EY9%+8D?=FZ=irTDIKAY zB>n})PkdaUkBi)oBJ($oQSC3Ze-2k@yajLZcQxLTUq!Bt#S@ETqh%Nv5_x}e>Vp(%)C zO8m&k$VPEa9O??%F)d?8K?1W9VpEpoEI787m(V(VQ(M&fa+*2QXY12BJ>&F^=+3N_ z9U$Z6SaRt4=-Ak}gq;CK!(6oHbf53MW@d9bxg*C7({N5m2z7Q%N(c{HSzSWiuwm+> zh50Gnp3tUpq{N4Hr!JM|Rpi{x`odFFM{9M@U2@V9(cdZ9m%H;h7 ztv8tL3ebF!K`oa{8IH)O#9TE6IL_S8=y}JmOi>xrvht(WDSJkOV(U)9HierKHU|Ve zw^#|m%#`K)lywAnDtNk=49k#%(E&fW5(224Cd+2{|$)3&w%e0DjYoaYxoQ- zMi(t3JL7G!EKl`?goe`DT1TjGg!%(0$XHZy3%x3~V~2{{Sd#IUinsBOjCWPMhxb){ zfDcuCgpXBxf=?y1l@u0@Swx~{Iz^ZW?>|#<5TBQEZxFDGFYu*`ukba;Yf1Ozg6SCZ zdcfzMxFca-iK9ZLioIBtHLqj@_6DL}@6?q}UHD4aUZPYm#o19hPqLF{L@&vUTCSjv zO|$Jf%P`_|up_pV#L90FjvUX9gk!;O@Txv=EdWxV6dZ^?iIXDF@$9&5H(_fTLam`Q z$XFb6n@WC660Mwj6Y23Y5^9WvaodYAKm4rsEQNiP>A9l_F65|FU_Svtk@p{wYf`!uPb zsMs2GT47Xtv3R&Y7}V~SYeSd(yGWTf%q#)s7#RuLdig-P{c#zfl)2_A+|bA{`-4aisfv!ok@s$_iIZ*})0NDsKF!fvh+ zmso>6+^Ze~_F^A@33XlMqHvYqQ%PHv`lAm~^()SCmFlBEB9snwr_$m5sdRNuDjhkH zO3R6GDjiK!r_%M2M1-r{B(EZzT0!+Mi1bJAL+OttVk?LyqQ7CHX9Y3w`yrHN)I3B@ zzj7ZP;#~a}n-Vc;6}5uYm>^Sl6`MkuvB}C!+Rc!9Bd5m~EQS%55{nYv$eH#WhbdQhdlYw9aFMr5FHi5F#Xr4^8tUJ{m6_z_aEM>A zJ4{QWG|RO1f=D)ZM@SQ8U8b+Sj6&>=QnuRN9ph=l-SyKWnVk1<94{e?6Qnj%hiCsJ z?Rz>-Qpd~OA3@|Fh8&a8F5{Gp)9yMbV~86`7I=^$N1mWn#$fyj>Ur`PHJ+htl@P=^ z=Fx*xf;92x!iinVtp}%*bmaURTi!(?(6)-kcvHNYm0m^53buTYn)|4|_!x;aLD~BA z12+o|v?d}F>YCkn<7gnro4K~2k!qXQ(Aq+OL=E7^CZlSS&d2CzlB6nJKqtQn)wc5= IoDL-Z1uw#r;s5{u literal 0 HcmV?d00001 diff --git a/apm-agent-core/src/main/resources/bootstrap/IndyBootstrap.java b/apm-agent-core/src/main/resources/bootstrap/IndyBootstrapDispatcher.java similarity index 64% rename from apm-agent-core/src/main/resources/bootstrap/IndyBootstrap.java rename to apm-agent-core/src/main/resources/bootstrap/IndyBootstrapDispatcher.java index fb6f1a02ee..aed1a54a91 100644 --- a/apm-agent-core/src/main/resources/bootstrap/IndyBootstrap.java +++ b/apm-agent-core/src/main/resources/bootstrap/IndyBootstrapDispatcher.java @@ -11,6 +11,16 @@ public class IndyBootstrap { public static Method bootstrap; + private static final MethodHandle VOID_NOOP; + + static { + try { + VOID_NOOP = MethodHandles.lookup().findStatic(IndyBootstrap.class, "voidNoop", MethodType.methodType(void.class)); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + public static CallSite bootstrap(MethodHandles.Lookup lookup, String adviceMethodName, MethodType adviceMethodType, @@ -35,11 +45,20 @@ public static CallSite bootstrap(MethodHandles.Lookup lookup, } if (callSite == null) { Class returnType = adviceMethodType.returnType(); - Object returnTypeDefaultValue; - returnTypeDefaultValue = Array.get(Array.newInstance(returnType, 1), 0); - MethodHandle noop = MethodHandles.dropArguments(MethodHandles.constant(returnType, returnTypeDefaultValue), 0, adviceMethodType.parameterList()); + MethodHandle noopNoArg; + if (returnType == void.class) { + noopNoArg = VOID_NOOP; + } else if (!returnType.isPrimitive()) { + noopNoArg = MethodHandles.constant(returnType, null); + } else { + noopNoArg = MethodHandles.constant(returnType, Array.get(Array.newInstance(returnType, 1), 0)); + } + MethodHandle noop = MethodHandles.dropArguments(noopNoArg, 0, adviceMethodType.parameterList()); callSite = new ConstantCallSite(noop); } return callSite; } + + public static void voidNoop() { + } } From 4b3aa01dcea64f6fade764dc588e60d9849518bb Mon Sep 17 00:00:00 2001 From: Felix Barnsteiner Date: Fri, 12 Jun 2020 08:57:59 +0200 Subject: [PATCH 11/35] Polish and documentation - Rename helper class loader to plugin class loader - Add GlobalVariables registry --- .../agent/bci/ElasticApmInstrumentation.java | 40 ++++++ .../co/elastic/apm/agent/bci/GlobalState.java | 7 +- .../apm/agent/bci/HelperClassManager.java | 22 +-- .../elastic/apm/agent/bci/IndyBootstrap.java | 135 ++++++++++++++++-- .../threadlocal/RemoveOnGetThreadLocal.java | 30 +++- .../threadlocal/ThreadLocalRegistry.java | 51 ------- .../co/elastic/apm/agent/util/CallDepth.java | 33 +++++ .../apm/agent/util/GlobalVariables.java | 65 +++++++++ ...asticsearchClientAsyncInstrumentation.java | 2 +- ...asticsearchClientAsyncInstrumentation.java | 2 +- .../agent/jdbc/ConnectionInstrumentation.java | 2 - .../apm/agent/jdbc/JdbcInstrumentation.java | 4 +- .../agent/jdbc/StatementInstrumentation.java | 5 - .../agent/jdbc/helper/JdbcGlobalState.java | 5 - .../{JdbcHelperImpl.java => JdbcHelper.java} | 4 +- .../jdbc/AbstractJdbcInstrumentationTest.java | 4 +- .../okhttp/OkHttp3ClientInstrumentation.java | 2 +- .../okhttp/OkHttpClientInstrumentation.java | 2 +- .../agent/servlet/AsyncInstrumentation.java | 16 +-- ...RequestStreamRecordingInstrumentation.java | 41 ++---- .../apm/agent/servlet/ServletApiAdvice.java | 9 +- .../servlet/ServletTransactionHelper.java | 15 -- .../ServletVersionInstrumentation.java | 80 ++++------- .../RecordingServletInputStreamWrapper.java | 7 +- .../ServletTransactionCreationHelper.java | 4 - .../agent/servlet/helper/package-info.java | 44 +----- 26 files changed, 361 insertions(+), 270 deletions(-) delete mode 100644 apm-agent-core/src/main/java/co/elastic/apm/agent/threadlocal/ThreadLocalRegistry.java create mode 100644 apm-agent-core/src/main/java/co/elastic/apm/agent/util/GlobalVariables.java rename apm-agent-plugins/apm-jdbc-plugin/src/main/java/co/elastic/apm/agent/jdbc/helper/{JdbcHelperImpl.java => JdbcHelper.java} (99%) diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/ElasticApmInstrumentation.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/ElasticApmInstrumentation.java index 17c6621a74..e657bc482c 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/ElasticApmInstrumentation.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/ElasticApmInstrumentation.java @@ -195,6 +195,46 @@ public Advice.OffsetMapping.Factory getOffsetMapping() { public void onTypeMatch(TypeDescription typeDescription, ClassLoader classLoader, ProtectionDomain protectionDomain, @Nullable Class classBeingRedefined) { } + /** + * When this method returns {@code true} the whole package (starting at the {@linkplain #getAdviceClass() advice's} package) + * will be loaded from a plugin class loader that has both the agent class loader and the class loader of the class this instruments as + * the parent. + *

+ * This instructs Byte Buddy to dispatch to the advice methods via an {@code INVOKEDYNAMIC} instruction. + * Upon first invocation of an instrumented method, + * this will call {@link IndyBootstrap#bootstrap} to determine the target {@link java.lang.invoke.ConstantCallSite}. + *

+ *

+ * Things to watch out for when using indy dispatch: + *

    + *
  • + * When an advice instruments classes in multiple class loaders, the plugin classes will be loaded form multiple class loaders. + * In order to still share state across those plugin class loaders, use {@link co.elastic.apm.agent.util.GlobalVariables} or {@link GlobalState}. + * That's necessary as a static variables are scoped to the class loader they are defined in. + *
  • + *
  • + * Don't use {@link ThreadLocal}s as it can lead to class loader leaks. + * Use {@link co.elastic.apm.agent.threadlocal.RemoveOnGetThreadLocal} instead. + *
  • + *
  • + * Due to the automatic plugin classloader creation that is based on package scanning, + * plugins need be in their own uniquely named package. + * As the package of the {@link #getAdviceClass()} is used as the root, + * all advices have to be at the top level of the plugin. + *
  • + *
  • + * Set {@link Advice.OnMethodEnter#inline()} and {@link Advice.OnMethodExit#inline()} to {@code false} on all advices. + * As the {@code readOnly} flag in Byte Buddy annotations such as {@link Advice.Return#readOnly()} cannot be used with non + * {@linkplain Advice.OnMethodEnter#inline() inlined advices}, + * use {@link co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignTo} and friends. + *
  • + *
+ *

+ * + * @return whether to load the classes of this plugin in dedicated plugin class loaders (one for each unique class loader) + * and dispatch to the {@linkplain #getAdviceClass() advice} via an {@code INVOKEDYNAMIC} instruction. + * @see IndyBootstrap + */ public boolean indyDispatch() { return false; } diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/GlobalState.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/GlobalState.java index 1527ec944e..0b83f22c4f 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/GlobalState.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/GlobalState.java @@ -11,9 +11,9 @@ * the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -40,6 +40,9 @@ * the instrumentation classes will also be loaded by multiple class loaders. * The effect of that is that state added to static variables in one class loader does not affect the static variable in other class loaders. *

+ *

+ * An alternative to this is {@link co.elastic.apm.agent.util.GlobalVariables} which can be used to make individual variables scoped globally. + *

*/ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/HelperClassManager.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/HelperClassManager.java index 72fd9afcab..ddb8575b4a 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/HelperClassManager.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/HelperClassManager.java @@ -276,7 +276,7 @@ private synchronized T loadAndReferenceHelper(Class classOfTargetClassLoader) } } - public static class ForDispatcher { + public static class ForIndyPlugin { private static final Map, WeakReference>> alreadyInjected = new WeakHashMap, WeakReference>>(); @@ -285,7 +285,7 @@ public static class ForDispatcher { * The agent class loader is currently the bootstrap CL but in the future it will be an isolated CL that is a child of the bootstrap CL. */ @Nullable - public synchronized static ClassLoader inject(@Nullable ClassLoader targetClassLoader, @Nullable ProtectionDomain protectionDomain, List classesToInject, ElementMatcher exclusionMatcher) throws Exception { + public synchronized static ClassLoader getOrCreatePluginClassLoader(@Nullable ClassLoader targetClassLoader, @Nullable ProtectionDomain protectionDomain, List classesToInject, ElementMatcher exclusionMatcher) throws Exception { classesToInject = new ArrayList<>(classesToInject); Map, WeakReference> injectedClasses = getOrCreateInjectedClasses(targetClassLoader); @@ -301,16 +301,16 @@ public synchronized static ClassLoader inject(@Nullable ClassLoader targetClassL classesToInjectCopy.add(className); } } - logger.debug("Creating helper class loader for {} containing {}", targetClassLoader, classesToInjectCopy); + logger.debug("Creating plugin class loader for {} containing {}", targetClassLoader, classesToInjectCopy); - ClassLoader parent = getHelperClassLoaderParent(targetClassLoader); + ClassLoader parent = getPluginClassLoaderParent(targetClassLoader); Map typeDefinitions = getTypeDefinitions(classesToInjectCopy); - // child first semantics are important here as the helper CL contains classes that are also present in the agent CL - ClassLoader helperCL = new ByteArrayClassLoader.ChildFirst(parent, true, typeDefinitions, ByteArrayClassLoader.PersistenceHandler.MANIFEST); - injectedClasses.put(classesToInject, new WeakReference<>(helperCL)); + // child first semantics are important here as the plugin CL contains classes that are also present in the agent CL + ClassLoader pluginClassLoader = new ByteArrayClassLoader.ChildFirst(parent, true, typeDefinitions, ByteArrayClassLoader.PersistenceHandler.MANIFEST); + injectedClasses.put(classesToInject, new WeakReference<>(pluginClassLoader)); - return helperCL; + return pluginClassLoader; } private static Map, WeakReference> getOrCreateInjectedClasses(@Nullable ClassLoader targetClassLoader) { @@ -322,13 +322,13 @@ private static Map, WeakReference> getOrCreateIn return injectedClasses; } - private static ClassLoader getHelperClassLoaderParent(@Nullable ClassLoader targetClassLoader) { + private static ClassLoader getPluginClassLoaderParent(@Nullable ClassLoader targetClassLoader) { ClassLoader agentClassLoader = HelperClassManager.class.getClassLoader(); if (agentClassLoader == null) { agentClassLoader = ClassLoader.getSystemClassLoader(); } - // the helper class loader has both, the agent class loader and the target class loader as the parent - // this is important so that the helper class loader has direct access to the agent class loader + // the plugin class loader has both, the agent class loader and the target class loader as the parent + // this is important so that the plugin class loader has direct access to the agent class loader // otherwise, filtering class loaders (like OSGi) have a chance to interfere return new MultipleParentClassLoader(Arrays.asList(agentClassLoader, targetClassLoader)); } diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/IndyBootstrap.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/IndyBootstrap.java index 05bd304e4f..8c3f4b9c2f 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/IndyBootstrap.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/IndyBootstrap.java @@ -30,6 +30,7 @@ import org.stagemonitor.util.IOUtils; import javax.annotation.Nullable; +import java.lang.invoke.CallSite; import java.lang.invoke.ConstantCallSite; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; @@ -45,11 +46,119 @@ /** * When {@link ElasticApmInstrumentation#indyDispatch()} returns {@code true}, - * we instruct byte buddy to dispatch {@linkplain Advice.OnMethodEnter#inline()} non-inlined advices} via an invokedynamic (indy) instruction. + * we instruct Byte Buddy (via {@link Advice.WithCustomMapping#bootstrap(java.lang.reflect.Method)}) + * to dispatch {@linkplain Advice.OnMethodEnter#inline() non-inlined advices} via an invokedynamic (indy) instruction. + * The target method is linked to a dynamically created plugin class loader that is specific to an instrumentation plugin + * and the class loader of the instrumented method. + *

+ * The first invocation of an {@code INVOKEDYNAMIC} causes the JVM to dynamically link a {@link CallSite}. + * In this case, it will use the {@link #bootstrap} method to do that. + * This will also create the plugin class loader. + *

+ *
+ *   Bootstrap CL ←──────────────────────────── Agent CL
+ *       ↑ └java.lang.IndyBootstrapDispatcher ─ ↑ ─→ └ {@link IndyBootstrap#bootstrap}
+ *     Ext/Platform CL               ↑          │       ╷
+ *       ↑                           ╷          │       ↓
+ *     System CL                     ╷          │ {@link HelperClassManager.ForIndyPlugin#getOrCreatePluginClassLoader}
+ *       ↑                           ╷          │       ╷
+ *     Common               linking of CallSite │       ╷
+ *     ↑    ↑             (on first invocation) │    creates
+ * WebApp1  WebApp2                  ╷          │       ╷
+ *          ↑ - InstrumentedClass    ╷          │       ╷
+ *          │                ╷       ╷          │       ╷
+ *          │                INVOKEDYNAMIC      │       ↓
+ *          └────────────────┼──────────────────Plugin CL
+ *                           └╶╶╶╶╶╶╶╶╶╶╶╶╶╶╶╶→ ├ AdviceClass
+ *                                              └ AdviceHelper
+ * Legend:
+ *  ╶╶→ method calls
+ *  ──→ class loader parent/child relationships
+ * 
+ *

+ * Advantages: + *

+ *
    + *
  • + * OSGi class loaders + * can't interfere with class loading. + * Instrumented classes only need to see java.lang.IndyBootstrapDispatcher. + * The actual advice class is invoked via {@code INVOKEDYNAMIC} instruction, + * basically a dynamically looked up {@link MethodHandle}. + *
  • + *
  • + * Performance: + * As the target {@link MethodHandle} is not changed after the initial lookup + * (we return a {@link ConstantCallSite} from {@link IndyBootstrap#bootstrap}), + * the JIT can easily inline the advice into the instrumented method. + *
  • + *
  • + * Runtime attachment + * This approach circumvents any OSGi issues even when attaching the agent at runtime. + * Setting the {@code org.osgi.framework.bootdelegation} property after the OSGi class loaders have already initialized has no effect. + * This is also a more holistic solution that also works for non-OSGi filtering class loaders. + *
  • + *
  • + * Runtime detachment: + * After un-instrumenting classes ({@link ElasticApmAgent#reset()}) and stopping all agent threads there should be no references + * to any Plugin CL or the Agent CL. + * This means the GC should be able to completely remove all loaded classes and class loaders of the agent, + * except for {@code java.lang.IndyBootstrapDispatcher}. + * This can be useful to completely remove/detach the agent at runtime or to update the agent version without restarting the application. + *
  • + *
  • + * Class visibility: + * The plugins can access both the specific types of the library they access and the agent classes as the Plugin CL + * has both the Agent CL and the CL of the instrumented class as its parent. + * Again, OSGi class loaders can't interfere here as both the Plugin CL and the Agent CL are under full control of the agent. + *
  • + *
  • + * Debugging advices: + * Advice classes can easily be debugged as they are not inlined in the instrumented methods. + *
  • + *
  • + * Unit testing: + * Classes loaded from the bootstrap class loader can be instrumented in unit tests. + *
  • + *
+ *

+ * Challenges: + *

+ *
    + *
  • + * As we're working with {@code INVOKEDYNAMIC} instructions that have only been introduced in Java 7, + * we have to patch classes we instrument that are compiled with Java 6 bytecode level (50) to Java 7 bytecode level (51). + * This involves re-computing stack frames and removing JSR instructions. + * See also {@link co.elastic.apm.agent.bci.ElasticApmAgent#applyAdvice}. + * This makes instrumentation a bit slower but it seems to work reliably, + * even when re-transforming classes (important for runtime attachment). + *
  • + *
  • + * The {@code INVOKEDYNAMIC} support of early Java 7 versions is not reliable. + * That's why we disable the agent on them. + * See also {@link AgentMain#isJavaVersionSupported} + *
  • + *
  • + * There are some things to watch out for when writing plugins, + * as explained in {@link ElasticApmInstrumentation#indyDispatch()} + *
  • + *
+ * @see ElasticApmInstrumentation#indyDispatch() */ public class IndyBootstrap { + + /** + * Starts with {@code java.lang} so that OSGi class loaders don't restrict access to it + */ private static final String INDY_BOOTSTRAP_CLASS_NAME = "java.lang.IndyBootstrapDispatcher"; + /** + * The class file of {@code java.lang.IndyBootstrapDispatcher}. + * Ends with {@code clazz} because if it ended with {@code clazz}, it would be loaded like a regular class. + */ private static final String INDY_BOOTSTRAP_RESOURCE = "bootstrap/IndyBootstrapDispatcher.clazz"; + /** + * Caches the names of classes that are defined within a package and it's subpackages + */ private static final ConcurrentMap> classesByPackage = new ConcurrentHashMap<>(); @Nullable static Method indyBootstrapMethod; @@ -71,6 +180,9 @@ public static Method getIndyBootstrapMethod() { } } + /** + * Injects the {@code java.lang.IndyBootstrapDispatcher} class into the bootstrap class loader if it wasn't already. + */ private static Class initIndyBootstrap() throws Exception { try { return Class.forName(INDY_BOOTSTRAP_CLASS_NAME, false, null); @@ -84,7 +196,6 @@ private static Class initIndyBootstrap() throws Exception { /** * Is called by {@code java.lang.IndyBootstrapDispatcher#bootstrap} via reflection. *

- *

* This is to make it impossible for OSGi or other filtering class loaders to restrict access to classes in the bootstrap class loader. * Normally, additional classes that have been injected have to be explicitly allowed via the {@code org.osgi.framework.bootdelegation} * system property. @@ -104,7 +215,7 @@ private static Class initIndyBootstrap() throws Exception { * The advice can access both agent types and the types of the instrumented library. *

*

- * Exceptions and {@code null} return values are handled by caller. + * Exceptions and {@code null} return values are handled by {@code java.lang.IndyBootstrapDispatcher#bootstrap}. *

*/ @Nullable @@ -118,15 +229,19 @@ public static ConstantCallSite bootstrap(MethodHandles.Lookup lookup, int enter) throws Exception { Class adviceClass = Class.forName(adviceClassName); String packageName = adviceClass.getPackage().getName(); - List helperClasses = classesByPackage.get(packageName); - if (helperClasses == null) { + List pluginClasses = classesByPackage.get(packageName); + if (pluginClasses == null) { classesByPackage.putIfAbsent(packageName, PackageScanner.getClassNames(packageName)); - helperClasses = classesByPackage.get(packageName); + pluginClasses = classesByPackage.get(packageName); } - ClassLoader helperClassLoader = HelperClassManager.ForDispatcher.inject(lookup.lookupClass().getClassLoader(), instrumentedType.getProtectionDomain(), helperClasses, isAnnotatedWith(named(GlobalState.class.getName()))); - if (helperClassLoader != null) { - Class adviceInHelperCL = helperClassLoader.loadClass(adviceClassName); - MethodHandle methodHandle = MethodHandles.lookup().findStatic(adviceInHelperCL, adviceMethodName, adviceMethodType); + ClassLoader pluginClassLoader = HelperClassManager.ForIndyPlugin.getOrCreatePluginClassLoader( + lookup.lookupClass().getClassLoader(), + instrumentedType.getProtectionDomain(), + pluginClasses, + isAnnotatedWith(named(GlobalState.class.getName()))); + if (pluginClassLoader != null) { + Class adviceInPluginCL = pluginClassLoader.loadClass(adviceClassName); + MethodHandle methodHandle = MethodHandles.lookup().findStatic(adviceInPluginCL, adviceMethodName, adviceMethodType); return new ConstantCallSite(methodHandle); } return null; diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/threadlocal/RemoveOnGetThreadLocal.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/threadlocal/RemoveOnGetThreadLocal.java index 6b04f7b7c3..46728d816c 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/threadlocal/RemoveOnGetThreadLocal.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/threadlocal/RemoveOnGetThreadLocal.java @@ -11,9 +11,9 @@ * the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -27,21 +27,37 @@ import com.blogspot.mydailyjava.weaklockfree.DetachedThreadLocal; import javax.annotation.Nullable; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; public class RemoveOnGetThreadLocal extends DetachedThreadLocal { + private static final ConcurrentMap> registry = new ConcurrentHashMap<>(); @Nullable private final T defaultValue; - public RemoveOnGetThreadLocal() { - this(null); - } - - public RemoveOnGetThreadLocal(@Nullable T defaultValue) { + private RemoveOnGetThreadLocal(@Nullable T defaultValue) { super(Cleaner.INLINE); this.defaultValue = defaultValue; } + public static RemoveOnGetThreadLocal get(Class adviceClass, String key) { + return get(adviceClass.getName() + "." + key, null); + } + + public static RemoveOnGetThreadLocal get(Class adviceClass, String key, @Nullable T defaultValue) { + return get(adviceClass.getName() + "." + key, defaultValue); + } + + private static RemoveOnGetThreadLocal get(String key, @Nullable T defaultValue) { + RemoveOnGetThreadLocal threadLocal = registry.get(key); + if (threadLocal == null) { + registry.putIfAbsent(key, new RemoveOnGetThreadLocal(defaultValue)); + threadLocal = registry.get(key); + } + return (RemoveOnGetThreadLocal) threadLocal; + } + @Nullable public T getAndRemove() { T value = get(); diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/threadlocal/ThreadLocalRegistry.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/threadlocal/ThreadLocalRegistry.java deleted file mode 100644 index 43f174a5c4..0000000000 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/threadlocal/ThreadLocalRegistry.java +++ /dev/null @@ -1,51 +0,0 @@ -/*- - * #%L - * Elastic APM Java agent - * %% - * Copyright (C) 2018 - 2020 Elastic and contributors - * %% - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * #L% - */ -package co.elastic.apm.agent.threadlocal; - - -import javax.annotation.Nullable; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; - -public class ThreadLocalRegistry { - private static final ConcurrentMap> registry = new ConcurrentHashMap<>(); - - public static RemoveOnGetThreadLocal get(String key) { - return get(key, null); - } - - public static RemoveOnGetThreadLocal get(Class adviceClass, String key, @Nullable T defaultValue) { - return get(adviceClass.getName() + "." + key); - } - - public static RemoveOnGetThreadLocal get(String key, @Nullable T defaultValue) { - RemoveOnGetThreadLocal threadLocal = registry.get(key); - if (threadLocal == null) { - registry.putIfAbsent(key, new RemoveOnGetThreadLocal(defaultValue)); - threadLocal = registry.get(key); - } - return (RemoveOnGetThreadLocal) threadLocal; - } -} diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/util/CallDepth.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/util/CallDepth.java index 8240e5f405..fc311d89a3 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/util/CallDepth.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/util/CallDepth.java @@ -24,6 +24,8 @@ */ package co.elastic.apm.agent.util; +import net.bytebuddy.asm.Advice; + import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicInteger; @@ -35,6 +37,9 @@ public class CallDepth { private static final ConcurrentMap registry = new ConcurrentHashMap<>(); private final ThreadLocal callDepthPerThread = new ThreadLocal(); + private CallDepth() { + } + public static CallDepth get(Class adviceClass) { // we want to return the same CallDepth instance even if the advice class has been loaded from different class loaders String key = adviceClass.getName(); @@ -61,9 +66,23 @@ public int increment() { return callDepthForCurrentThread.getAndIncrement(); } + /** + * Calls {@link #increment()} and returns {@code false} if this is the outer-most (non-nested) invocation. + * + * @return {@code false} if this is the outer-most (non-nested) invocation, {@code true} otherwise + */ + public boolean isNestedCallAndIncrement() { + return increment() != 0; + } + /** * Decrements and gets the call depth counter. * Returns {@code 0} if this is the outer-most (non-nested) invocation. + *

+ * Note: this should be the first thing called on exit advices. + * Also make sure to set {@link Advice.OnMethodExit#onThrowable()} to {@link Throwable}{@code .class}. + * This ensures we don't end up with inconsistent counts. + *

* * @return the call depth after it has been incremented */ @@ -72,4 +91,18 @@ public int decrement() { assert depth >= 0; return depth; } + + /** + * Calls {@link #decrement()} and returns {@code false} if this is the outer-most (non-nested) invocation. + *

+ * Note: this should be the first thing called on exit advices. + * Also make sure to set {@link Advice.OnMethodExit#onThrowable()} to {@link Throwable}{@code .class}. + * This ensures we don't end up with inconsistent counts. + *

+ * + * @return {@code false} if this is the outer-most (non-nested) invocation, {@code true} otherwise + */ + public boolean isNestedCallAndDecrement() { + return decrement() != 0; + } } diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/util/GlobalVariables.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/util/GlobalVariables.java new file mode 100644 index 0000000000..77d8791641 --- /dev/null +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/util/GlobalVariables.java @@ -0,0 +1,65 @@ +/*- + * #%L + * Elastic APM Java agent + * %% + * Copyright (C) 2018 - 2020 Elastic and contributors + * %% + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * #L% + */ +package co.elastic.apm.agent.util; + + +import co.elastic.apm.agent.impl.ElasticApmTracer; + +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +/** + * As the classes of an instrumentation plugins may be loaded from multiple plugin class loaders, + * there's a need to share global state between those class loaders. + *

+ * An alternative to this is {@link co.elastic.apm.agent.bci.GlobalState} which can be used to make a whole class scoped globally. + *

+ */ +public class GlobalVariables { + private static final ConcurrentMap registry = new ConcurrentHashMap<>(); + + /** + * Gets a global variable given an advice class an additional key which identifies the variable within the class. + * + * @param adviceClass the advice class which uses the global variable. + * @param key an additional key which identifies the variable within the class. + * @param defaultValue the default value of the global variable + * @param the type of the variable + * @return a global variable + */ + public static T get(Class adviceClass, String key, T defaultValue) { + key = adviceClass.getName() + "." + key; + if (defaultValue.getClass().getClassLoader() != null && !defaultValue.getClass().getName().startsWith(ElasticApmTracer.class.getPackage().getName())) { + throw new IllegalArgumentException("Registering types specific to an instrumentation plugin would lead to class loader leaks: " + defaultValue); + } + T value = (T) registry.get(key); + if (value == null) { + registry.putIfAbsent(key, defaultValue); + value = (T) registry.get(key); + } + return value; + } + +} diff --git a/apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-5_6/src/main/java/co/elastic/apm/agent/es/restclient/v5_6/ElasticsearchClientAsyncInstrumentation.java b/apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-5_6/src/main/java/co/elastic/apm/agent/es/restclient/v5_6/ElasticsearchClientAsyncInstrumentation.java index 630e5ddfb9..1b9988cb41 100644 --- a/apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-5_6/src/main/java/co/elastic/apm/agent/es/restclient/v5_6/ElasticsearchClientAsyncInstrumentation.java +++ b/apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-5_6/src/main/java/co/elastic/apm/agent/es/restclient/v5_6/ElasticsearchClientAsyncInstrumentation.java @@ -76,7 +76,7 @@ public ElementMatcher getMethodMatcher() { public static class ElasticsearchRestClientAsyncAdvice { - private static final RemoveOnGetThreadLocal spanTls = new RemoveOnGetThreadLocal<>(); + private static final RemoveOnGetThreadLocal spanTls = RemoveOnGetThreadLocal.get(ElasticsearchRestClientAsyncAdvice.class, "spanTls"); @AssignToArgument(5) @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) public static ResponseListener onBeforeExecute(@Advice.Argument(0) String method, diff --git a/apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-6_4/src/main/java/co/elastic/apm/agent/es/restclient/v6_4/ElasticsearchClientAsyncInstrumentation.java b/apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-6_4/src/main/java/co/elastic/apm/agent/es/restclient/v6_4/ElasticsearchClientAsyncInstrumentation.java index 83fa988279..12293448ab 100644 --- a/apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-6_4/src/main/java/co/elastic/apm/agent/es/restclient/v6_4/ElasticsearchClientAsyncInstrumentation.java +++ b/apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-6_4/src/main/java/co/elastic/apm/agent/es/restclient/v6_4/ElasticsearchClientAsyncInstrumentation.java @@ -70,7 +70,7 @@ public ElementMatcher getMethodMatcher() { } public static class ElasticsearchRestClientAsyncAdvice { - private static final RemoveOnGetThreadLocal spanTls = new RemoveOnGetThreadLocal<>(); + private static final RemoveOnGetThreadLocal spanTls = RemoveOnGetThreadLocal.get(ElasticsearchRestClientAsyncAdvice.class, "spanTls"); @AssignToArgument(1) @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) diff --git a/apm-agent-plugins/apm-jdbc-plugin/src/main/java/co/elastic/apm/agent/jdbc/ConnectionInstrumentation.java b/apm-agent-plugins/apm-jdbc-plugin/src/main/java/co/elastic/apm/agent/jdbc/ConnectionInstrumentation.java index 44d1604493..658b748dea 100644 --- a/apm-agent-plugins/apm-jdbc-plugin/src/main/java/co/elastic/apm/agent/jdbc/ConnectionInstrumentation.java +++ b/apm-agent-plugins/apm-jdbc-plugin/src/main/java/co/elastic/apm/agent/jdbc/ConnectionInstrumentation.java @@ -24,7 +24,6 @@ */ package co.elastic.apm.agent.jdbc; -import co.elastic.apm.agent.bci.VisibleForAdvice; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.NamedElement; import net.bytebuddy.description.method.MethodDescription; @@ -50,7 +49,6 @@ */ public class ConnectionInstrumentation extends JdbcInstrumentation { - @VisibleForAdvice @Advice.OnMethodExit(suppress = Throwable.class, inline = false) public static void storeSql(@Advice.Return PreparedStatement statement, @Advice.Argument(0) String sql) { diff --git a/apm-agent-plugins/apm-jdbc-plugin/src/main/java/co/elastic/apm/agent/jdbc/JdbcInstrumentation.java b/apm-agent-plugins/apm-jdbc-plugin/src/main/java/co/elastic/apm/agent/jdbc/JdbcInstrumentation.java index d79fa30bdc..f97cac4b3d 100644 --- a/apm-agent-plugins/apm-jdbc-plugin/src/main/java/co/elastic/apm/agent/jdbc/JdbcInstrumentation.java +++ b/apm-agent-plugins/apm-jdbc-plugin/src/main/java/co/elastic/apm/agent/jdbc/JdbcInstrumentation.java @@ -25,7 +25,7 @@ package co.elastic.apm.agent.jdbc; import co.elastic.apm.agent.bci.ElasticApmInstrumentation; -import co.elastic.apm.agent.jdbc.helper.JdbcHelperImpl; +import co.elastic.apm.agent.jdbc.helper.JdbcHelper; import java.util.Collection; import java.util.Collections; @@ -34,7 +34,7 @@ public abstract class JdbcInstrumentation extends ElasticApmInstrumentation { private static final Collection JDBC_GROUPS = Collections.singleton("jdbc"); - protected static JdbcHelperImpl jdbcHelper = new JdbcHelperImpl(); + protected static JdbcHelper jdbcHelper = new JdbcHelper(); @Override public final Collection getInstrumentationGroupNames() { diff --git a/apm-agent-plugins/apm-jdbc-plugin/src/main/java/co/elastic/apm/agent/jdbc/StatementInstrumentation.java b/apm-agent-plugins/apm-jdbc-plugin/src/main/java/co/elastic/apm/agent/jdbc/StatementInstrumentation.java index 4b5e7c7678..d401d318ce 100644 --- a/apm-agent-plugins/apm-jdbc-plugin/src/main/java/co/elastic/apm/agent/jdbc/StatementInstrumentation.java +++ b/apm-agent-plugins/apm-jdbc-plugin/src/main/java/co/elastic/apm/agent/jdbc/StatementInstrumentation.java @@ -24,7 +24,6 @@ */ package co.elastic.apm.agent.jdbc; -import co.elastic.apm.agent.bci.VisibleForAdvice; import co.elastic.apm.agent.impl.ElasticApmTracer; import co.elastic.apm.agent.impl.transaction.Span; import net.bytebuddy.asm.Advice; @@ -97,7 +96,6 @@ public ExecuteWithQueryInstrumentation(ElasticApmTracer tracer) { } @Nullable - @VisibleForAdvice @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) public static Span onBeforeExecute(@Advice.This Statement statement, @Advice.Argument(0) String sql) { @@ -110,7 +108,6 @@ public static Span onBeforeExecute(@Advice.This Statement statement, } - @VisibleForAdvice @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class, inline = false) public static void onAfterExecute(@Advice.This Statement statement, @Advice.Enter @Nullable Span span, @@ -150,7 +147,6 @@ public ExecuteUpdateWithQueryInstrumentation(ElasticApmTracer tracer) { } @Nullable - @VisibleForAdvice @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) public static Span onBeforeExecute(@Advice.This Statement statement, @Advice.Argument(0) String sql) { @@ -161,7 +157,6 @@ public static Span onBeforeExecute(@Advice.This Statement statement, return jdbcHelper.createJdbcSpan(sql, statement, tracer.getActive(), false); } - @VisibleForAdvice @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class, inline = false) public static void onAfterExecute(@Advice.Enter @Nullable Span span, @Advice.Thrown @Nullable Throwable t, diff --git a/apm-agent-plugins/apm-jdbc-plugin/src/main/java/co/elastic/apm/agent/jdbc/helper/JdbcGlobalState.java b/apm-agent-plugins/apm-jdbc-plugin/src/main/java/co/elastic/apm/agent/jdbc/helper/JdbcGlobalState.java index 92993a2ec9..4ac0b8b030 100644 --- a/apm-agent-plugins/apm-jdbc-plugin/src/main/java/co/elastic/apm/agent/jdbc/helper/JdbcGlobalState.java +++ b/apm-agent-plugins/apm-jdbc-plugin/src/main/java/co/elastic/apm/agent/jdbc/helper/JdbcGlobalState.java @@ -33,11 +33,6 @@ @GlobalState public class JdbcGlobalState { - // Important implementation note: - // - // because this class is potentially loaded from multiple classloaders, making those fields 'static' will not - // have the expected behavior, thus, any direct reference to `JdbcHelperImpl` should only be obtained from the - // HelperClassManager instance. public static final WeakConcurrentMap statementSqlMap = DataStructures.createWeakConcurrentMapWithCleanerThread(); public static final WeakConcurrentMap metaDataMap = DataStructures.createWeakConcurrentMapWithCleanerThread(); public static final WeakConcurrentMap, Boolean> metadataSupported = new WeakConcurrentMap.WithInlinedExpunction, Boolean>(); diff --git a/apm-agent-plugins/apm-jdbc-plugin/src/main/java/co/elastic/apm/agent/jdbc/helper/JdbcHelperImpl.java b/apm-agent-plugins/apm-jdbc-plugin/src/main/java/co/elastic/apm/agent/jdbc/helper/JdbcHelper.java similarity index 99% rename from apm-agent-plugins/apm-jdbc-plugin/src/main/java/co/elastic/apm/agent/jdbc/helper/JdbcHelperImpl.java rename to apm-agent-plugins/apm-jdbc-plugin/src/main/java/co/elastic/apm/agent/jdbc/helper/JdbcHelper.java index 8ed93bb63c..d50e39c3a3 100644 --- a/apm-agent-plugins/apm-jdbc-plugin/src/main/java/co/elastic/apm/agent/jdbc/helper/JdbcHelperImpl.java +++ b/apm-agent-plugins/apm-jdbc-plugin/src/main/java/co/elastic/apm/agent/jdbc/helper/JdbcHelper.java @@ -44,9 +44,9 @@ import static co.elastic.apm.agent.jdbc.helper.JdbcGlobalState.metadataSupported; import static co.elastic.apm.agent.jdbc.helper.JdbcGlobalState.statementSqlMap; -public class JdbcHelperImpl { +public class JdbcHelper { - private static final Logger logger = LoggerFactory.getLogger(JdbcHelperImpl.class); + private static final Logger logger = LoggerFactory.getLogger(JdbcHelper.class); public static final String DB_SPAN_TYPE = "db"; public static final String DB_SPAN_ACTION = "query"; diff --git a/apm-agent-plugins/apm-jdbc-plugin/src/test/java/co/elastic/apm/agent/jdbc/AbstractJdbcInstrumentationTest.java b/apm-agent-plugins/apm-jdbc-plugin/src/test/java/co/elastic/apm/agent/jdbc/AbstractJdbcInstrumentationTest.java index 11ec3e65be..64924c93cc 100644 --- a/apm-agent-plugins/apm-jdbc-plugin/src/test/java/co/elastic/apm/agent/jdbc/AbstractJdbcInstrumentationTest.java +++ b/apm-agent-plugins/apm-jdbc-plugin/src/test/java/co/elastic/apm/agent/jdbc/AbstractJdbcInstrumentationTest.java @@ -48,8 +48,8 @@ import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; -import static co.elastic.apm.agent.jdbc.helper.JdbcHelperImpl.DB_SPAN_ACTION; -import static co.elastic.apm.agent.jdbc.helper.JdbcHelperImpl.DB_SPAN_TYPE; +import static co.elastic.apm.agent.jdbc.helper.JdbcHelper.DB_SPAN_ACTION; +import static co.elastic.apm.agent.jdbc.helper.JdbcHelper.DB_SPAN_TYPE; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.assertj.core.api.Assertions.fail; diff --git a/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttp3ClientInstrumentation.java b/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttp3ClientInstrumentation.java index 7a17017a13..4ded66d88c 100644 --- a/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttp3ClientInstrumentation.java +++ b/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttp3ClientInstrumentation.java @@ -59,7 +59,7 @@ public Class getAdviceClass() { @VisibleForAdvice public static class OkHttpClient3ExecuteAdvice { - private final static RemoveOnGetThreadLocal spanTls = new RemoveOnGetThreadLocal<>(); + private final static RemoveOnGetThreadLocal spanTls = RemoveOnGetThreadLocal.get(OkHttpClient3ExecuteAdvice.class, "spanTls"); @Nullable @AssignToField(value = "originalRequest", typing = Assigner.Typing.DYNAMIC) diff --git a/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttpClientInstrumentation.java b/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttpClientInstrumentation.java index c1716f0b4a..3a7f38ee41 100644 --- a/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttpClientInstrumentation.java +++ b/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttpClientInstrumentation.java @@ -58,7 +58,7 @@ public Class getAdviceClass() { @VisibleForAdvice public static class OkHttpClientExecuteAdvice { - private final static RemoveOnGetThreadLocal spanTls = new RemoveOnGetThreadLocal<>(); + private final static RemoveOnGetThreadLocal spanTls = RemoveOnGetThreadLocal.get(OkHttpClientExecuteAdvice.class, "spanTls"); @Nullable @AssignToField(value = "originalRequest", typing = Assigner.Typing.DYNAMIC) diff --git a/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/AsyncInstrumentation.java b/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/AsyncInstrumentation.java index 17d2346c91..170a236e3d 100644 --- a/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/AsyncInstrumentation.java +++ b/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/AsyncInstrumentation.java @@ -24,7 +24,6 @@ */ package co.elastic.apm.agent.servlet; -import co.elastic.apm.agent.bci.VisibleForAdvice; import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToArgument; import co.elastic.apm.agent.impl.transaction.Transaction; import co.elastic.apm.agent.servlet.helper.AsyncContextAdviceHelperImpl; @@ -51,16 +50,6 @@ import static net.bytebuddy.matcher.ElementMatchers.takesArgument; import static net.bytebuddy.matcher.ElementMatchers.takesArguments; -/** - * Only the methods annotated with {@link Advice.OnMethodEnter} and {@link Advice.OnMethodExit} may contain references to - * {@code javax.servlet}, as these are inlined into the matching methods. - * The agent itself does not have access to the Servlet API classes, as they are loaded by a child class loader. - * See https://github.com/raphw/byte-buddy/issues/465 for more information. - * However, the helper class {@link AsyncContextAdviceHelper} has access to the Servlet API, - * as it is loaded by the child classloader of {@link AsyncContext} - * (see {@link StartAsyncInstrumentation.StartAsyncAdvice#onExitStartAsync(AsyncContext)} - * and {@link AsyncContextInstrumentation.AsyncContextStartAdvice#onEnterAsyncContextStart(Runnable)}). - */ public abstract class AsyncInstrumentation extends AbstractServletInstrumentation { private static final String SERVLET_API_ASYNC_GROUP_NAME = "servlet-api-async"; @@ -114,10 +103,8 @@ public Class getAdviceClass() { return StartAsyncAdvice.class; } - @VisibleForAdvice public static class StartAsyncAdvice { - @VisibleForAdvice - public static AsyncContextAdviceHelper asyncHelper = new AsyncContextAdviceHelperImpl(tracer); + private static final AsyncContextAdviceHelper asyncHelper = new AsyncContextAdviceHelperImpl(tracer); @Advice.OnMethodExit(suppress = Throwable.class, inline = false) public static void onExitStartAsync(@Advice.Return AsyncContext asyncContext) { @@ -151,7 +138,6 @@ public Class getAdviceClass() { return AsyncContextStartAdvice.class; } - @VisibleForAdvice public static class AsyncContextStartAdvice { @Nullable diff --git a/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/RequestStreamRecordingInstrumentation.java b/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/RequestStreamRecordingInstrumentation.java index 537764f739..0f043cafe6 100644 --- a/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/RequestStreamRecordingInstrumentation.java +++ b/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/RequestStreamRecordingInstrumentation.java @@ -24,16 +24,17 @@ */ package co.elastic.apm.agent.servlet; -import co.elastic.apm.agent.bci.VisibleForAdvice; import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToReturn; import co.elastic.apm.agent.impl.transaction.Transaction; import co.elastic.apm.agent.servlet.helper.RecordingServletInputStreamWrapper; +import co.elastic.apm.agent.util.CallDepth; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.NamedElement; import net.bytebuddy.description.method.MethodDescription; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.matcher.ElementMatcher; +import javax.annotation.Nullable; import javax.servlet.ServletInputStream; import java.util.Arrays; import java.util.Collection; @@ -75,38 +76,26 @@ public Class getAdviceClass() { public static class GetInputStreamAdvice { - @VisibleForAdvice - public static final ThreadLocal nestedThreadLocal = new ThreadLocal() { - @Override - protected Boolean initialValue() { - return Boolean.FALSE; - } - }; + private static final CallDepth callDepth = CallDepth.get(GetInputStreamAdvice.class); @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) - public static boolean onReadEnter(@Advice.This Object thiz) { - boolean nested = nestedThreadLocal.get(); - nestedThreadLocal.set(Boolean.TRUE); - return nested; + public static void onReadEnter(@Advice.This Object thiz) { + callDepth.increment(); } + @Nullable @AssignToReturn - @Advice.OnMethodExit(suppress = Throwable.class, inline = false) - public static ServletInputStream afterGetInputStream(@Advice.Return ServletInputStream inputStream, - @Advice.Enter boolean nested) { - if (nested == Boolean.TRUE || tracer == null) { + @Advice.OnMethodExit(suppress = Throwable.class, inline = false, onThrowable = Throwable.class) + public static ServletInputStream afterGetInputStream(@Advice.Return @Nullable ServletInputStream inputStream) { + if (callDepth.isNestedCallAndDecrement() || tracer == null || inputStream == null) { return inputStream; } - try { - final Transaction transaction = tracer.currentTransaction(); - // only wrap if the body buffer has been initialized via ServletTransactionHelper.startCaptureBody - if (transaction != null && transaction.getContext().getRequest().getBodyBuffer() != null) { - return new RecordingServletInputStreamWrapper(transaction.getContext().getRequest(), inputStream); - } else { - return inputStream; - } - } finally { - nestedThreadLocal.set(Boolean.FALSE); + final Transaction transaction = tracer.currentTransaction(); + // only wrap if the body buffer has been initialized via ServletTransactionHelper.startCaptureBody + if (transaction != null && transaction.getContext().getRequest().getBodyBuffer() != null) { + return new RecordingServletInputStreamWrapper(transaction.getContext().getRequest(), inputStream); + } else { + return inputStream; } } } diff --git a/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/ServletApiAdvice.java b/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/ServletApiAdvice.java index 8d8733f835..9ef1dcea2b 100644 --- a/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/ServletApiAdvice.java +++ b/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/ServletApiAdvice.java @@ -31,7 +31,6 @@ import co.elastic.apm.agent.impl.transaction.Transaction; import co.elastic.apm.agent.servlet.helper.ServletTransactionCreationHelper; import co.elastic.apm.agent.threadlocal.RemoveOnGetThreadLocal; -import co.elastic.apm.agent.threadlocal.ThreadLocalRegistry; import net.bytebuddy.asm.Advice; import javax.annotation.Nullable; @@ -54,12 +53,6 @@ import static co.elastic.apm.agent.servlet.ServletTransactionHelper.determineServiceName; import static java.lang.Boolean.FALSE; -/** - * Only the methods annotated with {@link Advice.OnMethodEnter} and {@link Advice.OnMethodExit} may contain references to - * {@code javax.servlet}, as these are inlined into the matching methods. - * The agent itself does not have access to the Servlet API classes, as they are loaded by a child class loader. - * See https://github.com/raphw/byte-buddy/issues/465 for more information. - */ public class ServletApiAdvice { private static final ServletTransactionHelper servletTransactionHelper; @@ -70,7 +63,7 @@ public class ServletApiAdvice { servletTransactionCreationHelper = new ServletTransactionCreationHelper(tracer); } - private static final RemoveOnGetThreadLocal excluded = ThreadLocalRegistry.get(ServletApiAdvice.class, "excluded", false); + private static final RemoveOnGetThreadLocal excluded = RemoveOnGetThreadLocal.get(ServletApiAdvice.class, "excluded", false); private static final List requestExceptionAttributes = Arrays.asList("javax.servlet.error.exception", "exception", "org.springframework.web.servlet.DispatcherServlet.EXCEPTION", "co.elastic.apm.exception"); @Nullable diff --git a/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/ServletTransactionHelper.java b/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/ServletTransactionHelper.java index 28fe34c676..e79361d693 100644 --- a/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/ServletTransactionHelper.java +++ b/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/ServletTransactionHelper.java @@ -25,7 +25,6 @@ package co.elastic.apm.agent.servlet; import co.elastic.apm.agent.bci.ElasticApmInstrumentation; -import co.elastic.apm.agent.bci.VisibleForAdvice; import co.elastic.apm.agent.configuration.CoreConfiguration; import co.elastic.apm.agent.impl.ElasticApmTracer; import co.elastic.apm.agent.impl.context.Request; @@ -50,17 +49,10 @@ import static co.elastic.apm.agent.impl.transaction.AbstractSpan.PRIO_LOW_LEVEL_FRAMEWORK; import static co.elastic.apm.agent.servlet.ServletGlobalState.nameInitialized; -/** - * This class must not import classes from {@code javax.servlet} due to class loader issues. - * See https://github.com/raphw/byte-buddy/issues/465 for more information. - */ -@VisibleForAdvice public class ServletTransactionHelper { - @VisibleForAdvice public static final String TRANSACTION_ATTRIBUTE = ServletApiAdvice.class.getName() + ".transaction"; - @VisibleForAdvice public static final String ASYNC_ATTRIBUTE = ServletApiAdvice.class.getName() + ".async"; private static final String CONTENT_TYPE_FROM_URLENCODED = "application/x-www-form-urlencoded"; @@ -72,13 +64,11 @@ public class ServletTransactionHelper { private final CoreConfiguration coreConfiguration; private final WebConfiguration webConfiguration; - @VisibleForAdvice public ServletTransactionHelper(ElasticApmTracer tracer) { this.coreConfiguration = tracer.getConfig(CoreConfiguration.class); this.webConfiguration = tracer.getConfig(WebConfiguration.class); } - @VisibleForAdvice public static void determineServiceName(@Nullable String servletContextName, ClassLoader servletContextClassLoader, @Nullable String contextPath) { if (ElasticApmInstrumentation.tracer == null || !nameInitialized.add(contextPath == null ? "null" : contextPath)) { return; @@ -102,7 +92,6 @@ public static void determineServiceName(@Nullable String servletContextName, Cla } } - @VisibleForAdvice public void fillRequestContext(Transaction transaction, String protocol, String method, boolean secure, String scheme, String serverName, int serverPort, String requestURI, String queryString, String remoteAddr, @Nullable String contentTypeHeader) { @@ -137,7 +126,6 @@ private void startCaptureBody(Transaction transaction, String method, @Nullable } } - @VisibleForAdvice public static void setUsernameIfUnset(@Nullable String userName, TransactionContext context) { // only set username if not manually set if (context.getUser().getUsername() == null) { @@ -145,7 +133,6 @@ public static void setUsernameIfUnset(@Nullable String userName, TransactionCont } } - @VisibleForAdvice public void onAfter(Transaction transaction, @Nullable Throwable exception, boolean committed, int status, boolean overrideStatusCodeOnThrowable, String method, @Nullable Map parameterMap, @Nullable String servletPath, @Nullable String pathInfo, @Nullable String contentTypeHeader, boolean deactivate) { @@ -229,7 +216,6 @@ private void fillRequestParameters(Transaction transaction, String method, @Null } } - @VisibleForAdvice public boolean captureParameters(String method, @Nullable String contentTypeHeader) { return contentTypeHeader != null && contentTypeHeader.startsWith(CONTENT_TYPE_FROM_URLENCODED) @@ -313,7 +299,6 @@ private String getHttpVersion(String protocol) { } } - @VisibleForAdvice public static void setTransactionNameByServletClass(@Nullable String method, @Nullable Class servletClass, Transaction transaction) { if (servletClass == null) { return; diff --git a/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/ServletVersionInstrumentation.java b/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/ServletVersionInstrumentation.java index 8ae61bbdfa..a68ac051eb 100644 --- a/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/ServletVersionInstrumentation.java +++ b/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/ServletVersionInstrumentation.java @@ -24,7 +24,7 @@ */ package co.elastic.apm.agent.servlet; -import co.elastic.apm.agent.bci.VisibleForAdvice; +import co.elastic.apm.agent.util.GlobalVariables; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.NamedElement; import net.bytebuddy.description.method.MethodDescription; @@ -39,6 +39,7 @@ import javax.servlet.ServletContext; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; +import java.util.concurrent.atomic.AtomicBoolean; import static net.bytebuddy.matcher.ElementMatchers.any; import static net.bytebuddy.matcher.ElementMatchers.hasSuperType; @@ -51,17 +52,12 @@ /** * Instruments {@link javax.servlet.Servlet} to log Servlet container details and warns about unsupported version. - *

- * Does not inherit from {@link AbstractServletInstrumentation} in order to still instrument when servlet version is not - * supported. */ public abstract class ServletVersionInstrumentation extends AbstractServletInstrumentation { - @VisibleForAdvice - public static final Logger logger = LoggerFactory.getLogger(ServletVersionInstrumentation.class); + private static final Logger logger = LoggerFactory.getLogger(ServletVersionInstrumentation.class); - @VisibleForAdvice - public static volatile boolean alreadyLogged = false; + private static final AtomicBoolean alreadyLogged = GlobalVariables.get(ServletVersionInstrumentation.class, "alreadyLogged", new AtomicBoolean(false)); @Override public ElementMatcher getTypeMatcherPreFilter() { @@ -93,27 +89,7 @@ public ElementMatcher getMethodMatcher() { @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) @SuppressWarnings("Duplicates") // duplication is fine here as it allows to inline code public static void onEnter(@Advice.Argument(0) @Nullable ServletConfig servletConfig) { - if (alreadyLogged) { - return; - } - alreadyLogged = true; - - int majorVersion = -1; - int minorVersion = -1; - String serverInfo = null; - if (servletConfig != null) { - ServletContext servletContext = servletConfig.getServletContext(); - if (null != servletContext) { - majorVersion = servletContext.getMajorVersion(); - minorVersion = servletContext.getMinorVersion(); - serverInfo = servletContext.getServerInfo(); - } - } - - logger.info("Servlet container info = {}", serverInfo); - if (majorVersion < 3) { - logger.warn("Unsupported servlet version detected: {}.{}, no Servlet transaction will be created", majorVersion, minorVersion); - } + logServletVersion(servletConfig); } } @@ -130,33 +106,33 @@ public ElementMatcher getMethodMatcher() { } @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) - @SuppressWarnings("Duplicates") // duplication is fine here as it allows to inline code public static void onEnter(@Advice.This Servlet servlet) { - if (alreadyLogged) { - return; - } - alreadyLogged = true; - - ServletConfig servletConfig = servlet.getServletConfig(); - - int majorVersion = -1; - int minorVersion = -1; - String serverInfo = null; - if (servletConfig != null) { - ServletContext servletContext = servletConfig.getServletContext(); - if (null != servletContext) { - majorVersion = servletContext.getMajorVersion(); - minorVersion = servletContext.getMinorVersion(); - serverInfo = servletContext.getServerInfo(); - } - } + logServletVersion(servlet.getServletConfig()); + } + } - logger.info("Servlet container info = {}", serverInfo); - if (majorVersion < 3) { - logger.warn("Unsupported servlet version detected: {}.{}, no Servlet transaction will be created", majorVersion, minorVersion); + private static void logServletVersion(@Nullable ServletConfig servletConfig) { + if (alreadyLogged.get()) { + return; + } + alreadyLogged.set(true); + + int majorVersion = -1; + int minorVersion = -1; + String serverInfo = null; + if (servletConfig != null) { + ServletContext servletContext = servletConfig.getServletContext(); + if (null != servletContext) { + majorVersion = servletContext.getMajorVersion(); + minorVersion = servletContext.getMinorVersion(); + serverInfo = servletContext.getServerInfo(); } } - } + logger.info("Servlet container info = {}", serverInfo); + if (majorVersion < 3) { + logger.warn("Unsupported servlet version detected: {}.{}, no Servlet transaction will be created", majorVersion, minorVersion); + } + } } diff --git a/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/helper/RecordingServletInputStreamWrapper.java b/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/helper/RecordingServletInputStreamWrapper.java index 5ebb7cba2e..19e21f5b65 100644 --- a/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/helper/RecordingServletInputStreamWrapper.java +++ b/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/helper/RecordingServletInputStreamWrapper.java @@ -11,9 +11,9 @@ * the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -24,7 +24,6 @@ */ package co.elastic.apm.agent.servlet.helper; -import co.elastic.apm.agent.bci.VisibleForAdvice; import co.elastic.apm.agent.impl.context.Request; import co.elastic.apm.agent.util.IOUtils; @@ -34,13 +33,11 @@ import java.nio.CharBuffer; import java.nio.charset.CoderResult; -@VisibleForAdvice public class RecordingServletInputStreamWrapper extends ServletInputStream { private final Request request; private final ServletInputStream servletInputStream; - @VisibleForAdvice public RecordingServletInputStreamWrapper(Request request, ServletInputStream servletInputStream) { this.request = request; this.servletInputStream = servletInputStream; diff --git a/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/helper/ServletTransactionCreationHelper.java b/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/helper/ServletTransactionCreationHelper.java index f9198cc839..5f6b35cc70 100644 --- a/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/helper/ServletTransactionCreationHelper.java +++ b/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/helper/ServletTransactionCreationHelper.java @@ -24,7 +24,6 @@ */ package co.elastic.apm.agent.servlet.helper; -import co.elastic.apm.agent.configuration.CoreConfiguration; import co.elastic.apm.agent.impl.ElasticApmTracer; import co.elastic.apm.agent.impl.context.web.WebConfiguration; import co.elastic.apm.agent.impl.transaction.Transaction; @@ -36,18 +35,15 @@ import javax.servlet.http.HttpServletRequest; import java.util.Objects; -@SuppressWarnings("unused") public class ServletTransactionCreationHelper { private static final Logger logger = LoggerFactory.getLogger(ServletTransactionCreationHelper.class); private final ElasticApmTracer tracer; - private final CoreConfiguration coreConfiguration; private final WebConfiguration webConfiguration; public ServletTransactionCreationHelper(ElasticApmTracer tracer) { this.tracer = tracer; - coreConfiguration = tracer.getConfig(CoreConfiguration.class); webConfiguration = tracer.getConfig(WebConfiguration.class); } diff --git a/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/helper/package-info.java b/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/helper/package-info.java index 9ca1f1ddae..37c5ddb961 100644 --- a/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/helper/package-info.java +++ b/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/helper/package-info.java @@ -11,9 +11,9 @@ * the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -22,46 +22,6 @@ * under the License. * #L% */ -/** - *

- * The classes in this package are loaded by a class loader which is a child of the class loader the Servlet API is loaded from. - * This works around the problem that the agent does not have access to the Servlet API. - * Instead of injecting the helper classes to the same class loader the Servlet API is loaded from, - * which will most likely not be possible anymore as of Java 11 - * (see http://jdk.java.net/11/release-notes#JDK-8193033), - * we create a class loader as the child of the one which loads the Servlet API. - *

- *

- * The classes loaded by this helper class loader can then access the Servlet API. - * However, the agent itself can't directly access the classes from the helper class loader, - * because they are loaded by a child class loader. - *

- *

- * This problem is circumvented by the agent providing an interface, - * which the helper class implements. - * The agent does then not need to know about the implementation type of the interface. - *

- *

- * One thing to be aware of is that the helper class loader needs to implement child first semantics when loading classes. - * Otherwise, it would load the helper implementation from the system or bootstrap classloader (where the agent is loaded from), - * without access to the Servlet API, - * instead of loading them itself so that the helper classes can access the Servlet API. - *

- *

- * Advices have to manually add their required helper classes to {@link co.elastic.apm.agent.bci.HelperClassManager}, - * which takes care of creating the helper class loaders. - *

- * - *
- *          System/Bootstrap CL (Agent)     provides interface AsyncContextAdviceHelper
- *                     |
- *                     v
- *             App CL (Servlet API)         uses AsyncContextAdviceHelperImpl from Helper CL
- *               /              \
- *              v               v
- * WebApp CL (user code/libs)   Helper CL   loads AsyncContextAdviceHelperImpl and ApmAsyncListener
- * 
- */ @NonnullApi package co.elastic.apm.agent.servlet.helper; From 98d5fb03ae049cd3361ee45f9ed2224361187dff Mon Sep 17 00:00:00 2001 From: Felix Barnsteiner Date: Fri, 12 Jun 2020 10:21:42 +0200 Subject: [PATCH 12/35] Add test for updating class file version while retransforming --- .../apm/agent/bci/InstrumentationTest.java | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/apm-agent-core/src/test/java/co/elastic/apm/agent/bci/InstrumentationTest.java b/apm-agent-core/src/test/java/co/elastic/apm/agent/bci/InstrumentationTest.java index dd0f089c0b..e58b3401ae 100644 --- a/apm-agent-core/src/test/java/co/elastic/apm/agent/bci/InstrumentationTest.java +++ b/apm-agent-core/src/test/java/co/elastic/apm/agent/bci/InstrumentationTest.java @@ -40,6 +40,7 @@ import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.matcher.ElementMatcher; import net.bytebuddy.matcher.ElementMatchers; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.math.util.MathUtils; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -50,6 +51,7 @@ import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; @@ -266,6 +268,25 @@ String exceptionPlease() { throw null; } + @Test + void testPatchClassFileVersionToJava7() { + // loading classes compiled with bytecode level 50 (Java 6) + assertThat(StringUtils.startsWithIgnoreCase("APM", "apm")).isTrue(); + + // retransforming classes and patch to bytecode level 51 (Java 7) + ElasticApmAgent.initInstrumentation(tracer, + ByteBuddyAgent.install(), + Collections.singletonList(new CommonsLangInstrumentation())); + + assertThat(CommonsLangInstrumentation.enterCount).hasValue(0); + assertThat(CommonsLangInstrumentation.exitCount).hasValue(0); + + assertThat(StringUtils.startsWithIgnoreCase("APM", "apm")).isTrue(); + + assertThat(CommonsLangInstrumentation.enterCount).hasPositiveValue(); + assertThat(CommonsLangInstrumentation.exitCount).hasPositiveValue(); + } + private void init(ConfigurationRegistry config, List instrumentations) { ElasticApmAgent.initInstrumentation(new ElasticApmTracerBuilder() .configurationRegistry(config) @@ -496,4 +517,40 @@ public Collection getInstrumentationGroupNames() { return List.of("test", "experimental"); } } + + public static class CommonsLangInstrumentation extends ElasticApmInstrumentation { + + static AtomicInteger enterCount = new AtomicInteger(); + static AtomicInteger exitCount = new AtomicInteger(); + + @Advice.OnMethodEnter(inline = false) + public static void onEnter() { + enterCount.incrementAndGet(); + } + + @Advice.OnMethodExit(inline = false) + public static void onExit() { + exitCount.incrementAndGet(); + } + + @Override + public ElementMatcher getTypeMatcher() { + return ElementMatchers.nameStartsWith(StringUtils.class.getPackageName()); + } + + @Override + public ElementMatcher getMethodMatcher() { + return ElementMatchers.any(); + } + + @Override + public Collection getInstrumentationGroupNames() { + return Collections.singletonList("test"); + } + + @Override + public boolean indyDispatch() { + return true; + } + } } From 019ad45964dc6c2411d22be3cc34087d9b027df4 Mon Sep 17 00:00:00 2001 From: Felix Barnsteiner Date: Sun, 14 Jun 2020 09:54:09 +0200 Subject: [PATCH 13/35] Add tests for patching class file version Some are currently failing see https://github.com/raphw/byte-buddy/issues/880 --- apm-agent-core/pom.xml | 12 + .../apm/agent/bci/ElasticApmAgent.java | 12 +- .../apm/agent/bci/HelperClassManager.java | 11 +- .../elastic/apm/agent/bci/IndyBootstrap.java | 10 +- .../apm/agent/util/PackageScanner.java | 6 +- .../apm/agent/bci/InstrumentationTest.java | 268 ++++++++++++++++-- .../apm/agent/util/PackageScannerTest.java | 53 ++++ .../latest/testapp/generated/HelloGrpc.java | 2 +- 8 files changed, 335 insertions(+), 39 deletions(-) create mode 100644 apm-agent-core/src/test/java/co/elastic/apm/agent/util/PackageScannerTest.java diff --git a/apm-agent-core/pom.xml b/apm-agent-core/pom.xml index 436d132ce8..d6c44b10f9 100644 --- a/apm-agent-core/pom.xml +++ b/apm-agent-core/pom.xml @@ -144,6 +144,18 @@ ${version.cucumber} test
+ + org.apache.commons + commons-math3 + 3.2 + test + + + org.apache.commons + commons-pool2 + 2.6.2 + test + diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/ElasticApmAgent.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/ElasticApmAgent.java index 570e60a876..20cabb3621 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/ElasticApmAgent.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/ElasticApmAgent.java @@ -451,9 +451,11 @@ public static synchronized void reset() { } dynamicClassFileTransformers.clear(); instrumentation = null; + HelperClassManager.ForIndyPlugin.clear(); } - private static AgentBuilder getAgentBuilder(final ByteBuddy byteBuddy, final CoreConfiguration coreConfiguration, Logger logger, AgentBuilder.DescriptionStrategy descriptionStrategy, boolean premain) { + private static AgentBuilder getAgentBuilder(final ByteBuddy byteBuddy, final CoreConfiguration coreConfiguration, final Logger logger, + final AgentBuilder.DescriptionStrategy descriptionStrategy, final boolean premain) { AgentBuilder.LocationStrategy locationStrategy = AgentBuilder.LocationStrategy.ForClassLoader.WEAK; if (agentJarFile != null) { try { @@ -475,6 +477,14 @@ private static AgentBuilder getAgentBuilder(final ByteBuddy byteBuddy, final Cor // when runtime attaching, only retransform up to 100 classes at once and sleep 100ms in-between as retransformation causes a stop-the-world pause .with(premain ? RedefinitionStrategy.BatchAllocator.ForTotal.INSTANCE : RedefinitionStrategy.BatchAllocator.ForFixedSize.ofSize(100)) .with(premain ? RedefinitionStrategy.Listener.NoOp.INSTANCE : RedefinitionStrategy.Listener.Pausing.of(100, TimeUnit.MILLISECONDS)) + .with(new RedefinitionStrategy.Listener.Adapter() { + @Override + public Iterable>> onError(int index, List> batch, Throwable throwable, List> types) { + logger.warn("Error while redefining classes {}", throwable.getMessage()); + logger.debug(throwable.getMessage(), throwable); + return super.onError(index, batch, throwable, types); + } + }) .with(descriptionStrategy) .with(locationStrategy) .with(new ErrorLoggingListener()) diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/HelperClassManager.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/HelperClassManager.java index ddb8575b4a..9b1cc2a379 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/HelperClassManager.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/HelperClassManager.java @@ -284,13 +284,17 @@ public static class ForIndyPlugin { * Creates an isolated CL that has two parents: the target class loader and the agent CL. * The agent class loader is currently the bootstrap CL but in the future it will be an isolated CL that is a child of the bootstrap CL. */ - @Nullable - public synchronized static ClassLoader getOrCreatePluginClassLoader(@Nullable ClassLoader targetClassLoader, @Nullable ProtectionDomain protectionDomain, List classesToInject, ElementMatcher exclusionMatcher) throws Exception { + public synchronized static ClassLoader getOrCreatePluginClassLoader(@Nullable ClassLoader targetClassLoader, List classesToInject, ElementMatcher exclusionMatcher) throws Exception { classesToInject = new ArrayList<>(classesToInject); Map, WeakReference> injectedClasses = getOrCreateInjectedClasses(targetClassLoader); if (injectedClasses.containsKey(classesToInject)) { - return injectedClasses.get(classesToInject).get(); + ClassLoader pluginClassLoader = injectedClasses.get(classesToInject).get(); + if (pluginClassLoader == null) { + injectedClasses.remove(classesToInject); + } else { + return pluginClassLoader; + } } List classesToInjectCopy = new ArrayList<>(classesToInject.size()); @@ -309,7 +313,6 @@ public synchronized static ClassLoader getOrCreatePluginClassLoader(@Nullable Cl ClassLoader pluginClassLoader = new ByteArrayClassLoader.ChildFirst(parent, true, typeDefinitions, ByteArrayClassLoader.PersistenceHandler.MANIFEST); injectedClasses.put(classesToInject, new WeakReference<>(pluginClassLoader)); - return pluginClassLoader; } diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/IndyBootstrap.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/IndyBootstrap.java index 8c3f4b9c2f..e94815c4f2 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/IndyBootstrap.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/IndyBootstrap.java @@ -236,14 +236,10 @@ public static ConstantCallSite bootstrap(MethodHandles.Lookup lookup, } ClassLoader pluginClassLoader = HelperClassManager.ForIndyPlugin.getOrCreatePluginClassLoader( lookup.lookupClass().getClassLoader(), - instrumentedType.getProtectionDomain(), pluginClasses, isAnnotatedWith(named(GlobalState.class.getName()))); - if (pluginClassLoader != null) { - Class adviceInPluginCL = pluginClassLoader.loadClass(adviceClassName); - MethodHandle methodHandle = MethodHandles.lookup().findStatic(adviceInPluginCL, adviceMethodName, adviceMethodType); - return new ConstantCallSite(methodHandle); - } - return null; + Class adviceInPluginCL = pluginClassLoader.loadClass(adviceClassName); + MethodHandle methodHandle = MethodHandles.lookup().findStatic(adviceInPluginCL, adviceMethodName, adviceMethodType); + return new ConstantCallSite(methodHandle); } } diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/util/PackageScanner.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/util/PackageScanner.java index 2752c84155..6e2807e535 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/util/PackageScanner.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/util/PackageScanner.java @@ -67,11 +67,7 @@ public static List getClassNames(final String basePackage) throws IOExce } } } else { - final Path basePath = Paths.get(uri); - if (basePath.toString().contains("test-classes")) { - continue; - } - classNames.addAll(listClassNames(basePackage, basePath)); + classNames.addAll(listClassNames(basePackage, Paths.get(uri))); } } return classNames; diff --git a/apm-agent-core/src/test/java/co/elastic/apm/agent/bci/InstrumentationTest.java b/apm-agent-core/src/test/java/co/elastic/apm/agent/bci/InstrumentationTest.java index e58b3401ae..0f364ed610 100644 --- a/apm-agent-core/src/test/java/co/elastic/apm/agent/bci/InstrumentationTest.java +++ b/apm-agent-core/src/test/java/co/elastic/apm/agent/bci/InstrumentationTest.java @@ -34,27 +34,38 @@ import co.elastic.apm.agent.impl.ElasticApmTracer; import co.elastic.apm.agent.impl.ElasticApmTracerBuilder; import co.elastic.apm.agent.matcher.WildcardMatcher; +import co.elastic.apm.agent.util.GlobalVariables; import net.bytebuddy.agent.ByteBuddyAgent; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.method.MethodDescription; import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.dynamic.loading.ByteArrayClassLoader; import net.bytebuddy.matcher.ElementMatcher; import net.bytebuddy.matcher.ElementMatchers; import org.apache.commons.lang3.StringUtils; import org.apache.commons.math.util.MathUtils; +import org.apache.commons.math3.stat.StatUtils; +import org.apache.commons.pool2.impl.CallStackUtils; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.slf4j.event.SubstituteLoggingEvent; import org.stagemonitor.configuration.ConfigurationRegistry; import javax.annotation.Nullable; +import java.lang.ref.WeakReference; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; +import static net.bytebuddy.matcher.ElementMatchers.any; +import static net.bytebuddy.matcher.ElementMatchers.isConstructor; +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.takesArguments; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.awaitility.Awaitility.await; import static org.mockito.Mockito.when; class InstrumentationTest { @@ -269,7 +280,7 @@ String exceptionPlease() { } @Test - void testPatchClassFileVersionToJava7() { + void testPatchClassFileVersionJava6ToJava7() { // loading classes compiled with bytecode level 50 (Java 6) assertThat(StringUtils.startsWithIgnoreCase("APM", "apm")).isTrue(); @@ -287,6 +298,84 @@ void testPatchClassFileVersionToJava7() { assertThat(CommonsLangInstrumentation.exitCount).hasPositiveValue(); } + @Test + void testPatchClassFileVersionJava5ToJava7() { + // loading classes compiled with bytecode level 49 (Java 6) + new org.slf4j.event.SubstituteLoggingEvent(); + + // retransforming classes and patch to bytecode level 51 (Java 7) + ElasticApmAgent.initInstrumentation(tracer, + ByteBuddyAgent.install(), + Collections.singletonList(new LoggerFactoryInstrumentation())); + + assertThat(LoggerFactoryInstrumentation.enterCount).hasValue(0); + assertThat(LoggerFactoryInstrumentation.exitCount).hasValue(0); + + new org.slf4j.event.SubstituteLoggingEvent(); + + assertThat(LoggerFactoryInstrumentation.enterCount).hasPositiveValue(); + assertThat(LoggerFactoryInstrumentation.exitCount).hasPositiveValue(); + } + + @Test + void testPatchClassFileVersionJava5ToJava7CommonsMath() { + org.apache.commons.math3.stat.StatUtils.max(new double[]{3.14}); + + // retransforming classes and patch to bytecode level 51 (Java 7) + ElasticApmAgent.initInstrumentation(tracer, + ByteBuddyAgent.install(), + Collections.singletonList(new StatUtilsInstrumentation())); + + assertThat(StatUtilsInstrumentation.enterCount).hasValue(0); + assertThat(StatUtilsInstrumentation.exitCount).hasValue(0); + + org.apache.commons.math3.stat.StatUtils.max(new double[]{3.14}); + + assertThat(StatUtilsInstrumentation.enterCount).hasPositiveValue(); + assertThat(StatUtilsInstrumentation.exitCount).hasPositiveValue(); + } + + @Test + void testPrivateConstructorJava7() { + org.apache.commons.pool2.impl.CallStackUtils.newCallStack("", false, false); + + // retransforming classes and patch to bytecode level 51 (Java 7) + ElasticApmAgent.initInstrumentation(tracer, + ByteBuddyAgent.install(), + Collections.singletonList(new CallStackUtilsInstrumentation())); + + assertThat(CallStackUtilsInstrumentation.enterCount).hasValue(0); + assertThat(CallStackUtilsInstrumentation.exitCount).hasValue(0); + + org.apache.commons.pool2.impl.CallStackUtils.newCallStack("", false, false); + + assertThat(CallStackUtilsInstrumentation.enterCount).hasPositiveValue(); + assertThat(CallStackUtilsInstrumentation.exitCount).hasPositiveValue(); + } + + @Test + void testPluginClassLoaderGCdAfterUndoingInstrumentation() { + ElasticApmAgent.initInstrumentation(tracer, + ByteBuddyAgent.install(), + Collections.singletonList(new ClassLoadingTestInstrumentation())); + + WeakReference pluginClassLoader = new WeakReference<>(getPluginClassLoader()); + assertThat(pluginClassLoader.get()).isNotNull(); + assertThat(pluginClassLoader.get()).isInstanceOf(ByteArrayClassLoader.ChildFirst.class); + + ElasticApmAgent.reset(); + assertThat(getPluginClassLoader()).isNull(); + + System.gc(); + System.gc(); + await().untilAsserted(() -> assertThat(pluginClassLoader.get()).isNull()); + } + + @Nullable + public ClassLoader getPluginClassLoader() { + return null; + } + private void init(ConfigurationRegistry config, List instrumentations) { ElasticApmAgent.initInstrumentation(new ElasticApmTracerBuilder() .configurationRegistry(config) @@ -308,12 +397,12 @@ public static String onMethodExit() { @Override public ElementMatcher getTypeMatcher() { - return ElementMatchers.named("co.elastic.apm.agent.bci.InstrumentationTest"); + return named("co.elastic.apm.agent.bci.InstrumentationTest"); } @Override public ElementMatcher getMethodMatcher() { - return ElementMatchers.named("interceptMe"); + return named("interceptMe"); } @Override @@ -331,12 +420,12 @@ public static int onMethodExit() { @Override public ElementMatcher getTypeMatcher() { - return ElementMatchers.named("org.apache.commons.math.util.MathUtils"); + return named("org.apache.commons.math.util.MathUtils"); } @Override public ElementMatcher getMethodMatcher() { - return ElementMatchers.named("sign").and(ElementMatchers.takesArguments(int.class)); + return named("sign").and(takesArguments(int.class)); } @Override @@ -353,7 +442,7 @@ public static void onMethodExit() { @Override public ElementMatcher getTypeMatcher() { - return ElementMatchers.named(InstrumentationTest.class.getName()); + return named(InstrumentationTest.class.getName()); } @Override @@ -381,12 +470,12 @@ public static String onMethodExit(@Advice.Thrown Throwable throwable) { @Override public ElementMatcher getTypeMatcher() { - return ElementMatchers.named(InstrumentationTest.class.getName()); + return named(InstrumentationTest.class.getName()); } @Override public ElementMatcher getMethodMatcher() { - return ElementMatchers.named("noExceptionPlease"); + return named("noExceptionPlease"); } @Override @@ -405,12 +494,12 @@ public static String onEnter(@Advice.Argument(0) String s) { @Override public ElementMatcher getTypeMatcher() { - return ElementMatchers.named("co.elastic.apm.agent.bci.InstrumentationTest"); + return named("co.elastic.apm.agent.bci.InstrumentationTest"); } @Override public ElementMatcher getMethodMatcher() { - return ElementMatchers.named("assignToField"); + return named("assignToField"); } @Override @@ -429,12 +518,12 @@ public static Object[] onEnter(@Advice.Argument(0) String s) { @Override public ElementMatcher getTypeMatcher() { - return ElementMatchers.named("co.elastic.apm.agent.bci.InstrumentationTest"); + return named("co.elastic.apm.agent.bci.InstrumentationTest"); } @Override public ElementMatcher getMethodMatcher() { - return ElementMatchers.named("assignToField"); + return named("assignToField"); } @Override @@ -453,12 +542,12 @@ public static String onEnter(@Advice.Argument(0) String s) { @Override public ElementMatcher getTypeMatcher() { - return ElementMatchers.named("co.elastic.apm.agent.bci.InstrumentationTest"); + return named("co.elastic.apm.agent.bci.InstrumentationTest"); } @Override public ElementMatcher getMethodMatcher() { - return ElementMatchers.named("assignToArgument"); + return named("assignToArgument"); } @Override @@ -480,12 +569,12 @@ public static Object[] onEnter(@Advice.Argument(0) String foo, @Advice.Argument( @Override public ElementMatcher getTypeMatcher() { - return ElementMatchers.named("co.elastic.apm.agent.bci.InstrumentationTest"); + return named("co.elastic.apm.agent.bci.InstrumentationTest"); } @Override public ElementMatcher getMethodMatcher() { - return ElementMatchers.named("assignToArguments"); + return named("assignToArguments"); } @Override @@ -504,12 +593,12 @@ public static Object[] onEnter(@Advice.Argument(0) String foo, @Advice.Argument( @Override public ElementMatcher getTypeMatcher() { - return ElementMatchers.named("co.elastic.apm.agent.bci.InstrumentationTest"); + return named("co.elastic.apm.agent.bci.InstrumentationTest"); } @Override public ElementMatcher getMethodMatcher() { - return ElementMatchers.named("assignToReturn"); + return named("assignToReturn"); } @Override @@ -520,8 +609,8 @@ public Collection getInstrumentationGroupNames() { public static class CommonsLangInstrumentation extends ElasticApmInstrumentation { - static AtomicInteger enterCount = new AtomicInteger(); - static AtomicInteger exitCount = new AtomicInteger(); + public static AtomicInteger enterCount = GlobalVariables.get(CommonsLangInstrumentation.class, "enterCount", new AtomicInteger()); + public static AtomicInteger exitCount = GlobalVariables.get(CommonsLangInstrumentation.class, "exitCount", new AtomicInteger()); @Advice.OnMethodEnter(inline = false) public static void onEnter() { @@ -540,7 +629,144 @@ public ElementMatcher getTypeMatcher() { @Override public ElementMatcher getMethodMatcher() { - return ElementMatchers.any(); + return any(); + } + + @Override + public Collection getInstrumentationGroupNames() { + return Collections.singletonList("test"); + } + + @Override + public boolean indyDispatch() { + return true; + } + } + + public static class LoggerFactoryInstrumentation extends ElasticApmInstrumentation { + + public static AtomicInteger enterCount = GlobalVariables.get(CommonsLangInstrumentation.class, "enterCount", new AtomicInteger()); + public static AtomicInteger exitCount = GlobalVariables.get(CommonsLangInstrumentation.class, "exitCount", new AtomicInteger()); + + @Advice.OnMethodEnter(inline = false) + public static void onEnter() { + enterCount.incrementAndGet(); + } + + @Advice.OnMethodExit(inline = false) + public static void onExit() { + exitCount.incrementAndGet(); + } + + @Override + public ElementMatcher getTypeMatcher() { + return named(SubstituteLoggingEvent.class.getName()); + } + + @Override + public ElementMatcher getMethodMatcher() { + return isConstructor(); + } + + @Override + public Collection getInstrumentationGroupNames() { + return Collections.singletonList("test"); + } + + @Override + public boolean indyDispatch() { + return true; + } + } + + public static class StatUtilsInstrumentation extends ElasticApmInstrumentation { + + public static AtomicInteger enterCount = GlobalVariables.get(CommonsLangInstrumentation.class, "enterCount", new AtomicInteger()); + public static AtomicInteger exitCount = GlobalVariables.get(CommonsLangInstrumentation.class, "exitCount", new AtomicInteger()); + + @Advice.OnMethodEnter(inline = false) + public static void onEnter() { + enterCount.incrementAndGet(); + } + + @Advice.OnMethodExit(inline = false) + public static void onExit() { + exitCount.incrementAndGet(); + } + + @Override + public ElementMatcher getTypeMatcher() { + return named(StatUtils.class.getName()); + } + + @Override + public ElementMatcher getMethodMatcher() { + return any(); + } + + @Override + public Collection getInstrumentationGroupNames() { + return Collections.singletonList("test"); + } + + @Override + public boolean indyDispatch() { + return true; + } + } + + public static class CallStackUtilsInstrumentation extends ElasticApmInstrumentation { + + public static AtomicInteger enterCount = GlobalVariables.get(CommonsLangInstrumentation.class, "enterCount", new AtomicInteger()); + public static AtomicInteger exitCount = GlobalVariables.get(CommonsLangInstrumentation.class, "exitCount", new AtomicInteger()); + + @Advice.OnMethodEnter(inline = false) + public static void onEnter() { + enterCount.incrementAndGet(); + } + + @Advice.OnMethodExit(inline = false) + public static void onExit() { + exitCount.incrementAndGet(); + } + + @Override + public ElementMatcher getTypeMatcher() { + return named(CallStackUtils.class.getName()); + } + + @Override + public ElementMatcher getMethodMatcher() { + return any(); + } + + @Override + public Collection getInstrumentationGroupNames() { + return Collections.singletonList("test"); + } + + @Override + public boolean indyDispatch() { + return true; + } + } + + public static class ClassLoadingTestInstrumentation extends ElasticApmInstrumentation { + + @AssignToReturn + @Advice.OnMethodExit(inline = false) + public static ClassLoader onExit() { + return ClassLoadingTestInstrumentation.class.getClassLoader(); + } + + @Override + public ElementMatcher getTypeMatcher() { + return named("co.elastic.apm.agent.bci.InstrumentationTest"); + } + + @Override + public ElementMatcher getMethodMatcher() { + return named("getPluginClassLoader"); } @Override diff --git a/apm-agent-core/src/test/java/co/elastic/apm/agent/util/PackageScannerTest.java b/apm-agent-core/src/test/java/co/elastic/apm/agent/util/PackageScannerTest.java new file mode 100644 index 0000000000..8cddb76b3a --- /dev/null +++ b/apm-agent-core/src/test/java/co/elastic/apm/agent/util/PackageScannerTest.java @@ -0,0 +1,53 @@ +/*- + * #%L + * Elastic APM Java agent + * %% + * Copyright (C) 2018 - 2020 Elastic and contributors + * %% + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * #L% + */ +package co.elastic.apm.agent.util; + +import net.bytebuddy.ByteBuddy; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class PackageScannerTest { + + @Test + void getClassNames() throws Exception { + assertThat(PackageScanner.getClassNames(getClass().getPackageName())) + .contains(PackageScanner.class.getName()); + } + + @Test + void testScanJar() throws Exception { + assertThat(PackageScanner.getClassNames(ByteBuddy.class.getPackageName())) + .contains(ByteBuddy.class.getName()); + // scan again to see verify there's no FileSystemAlreadyExistsException + assertThat(PackageScanner.getClassNames(ByteBuddy.class.getPackageName())) + .contains(ByteBuddy.class.getName()); + } + + @Test + void getClassNamesOfNonExistentPackage() throws Exception { + assertThat(PackageScanner.getClassNames("foo.bar")).isEmpty(); + } +} diff --git a/apm-agent-plugins/apm-grpc/apm-grpc-test-latest/src/test/java/co/elastic/apm/agent/grpc/latest/testapp/generated/HelloGrpc.java b/apm-agent-plugins/apm-grpc/apm-grpc-test-latest/src/test/java/co/elastic/apm/agent/grpc/latest/testapp/generated/HelloGrpc.java index ec410def33..682a0c4b7b 100644 --- a/apm-agent-plugins/apm-grpc/apm-grpc-test-latest/src/test/java/co/elastic/apm/agent/grpc/latest/testapp/generated/HelloGrpc.java +++ b/apm-agent-plugins/apm-grpc/apm-grpc-test-latest/src/test/java/co/elastic/apm/agent/grpc/latest/testapp/generated/HelloGrpc.java @@ -42,7 +42,7 @@ /** */ @javax.annotation.Generated( - value = "by gRPC proto compiler (version 1.29.0)", + value = "by gRPC proto compiler (version 1.30.0)", comments = "Source: rpc.proto") public final class HelloGrpc { From 90d1320577c39b52b193106038592d413b9df569 Mon Sep 17 00:00:00 2001 From: Felix Barnsteiner Date: Sun, 14 Jun 2020 15:49:00 +0200 Subject: [PATCH 14/35] Add validations for indy advices --- .../apm/agent/bci/ElasticApmAgent.java | 58 +++++++++++++++-- .../apm/agent/bci/InstrumentationTest.java | 50 +++++++++++++++ .../AdviceInSubpackageInstrumentation.java | 63 +++++++++++++++++++ 3 files changed, 165 insertions(+), 6 deletions(-) create mode 100644 apm-agent-core/src/test/java/co/elastic/apm/agent/bci/subpackage/AdviceInSubpackageInstrumentation.java diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/ElasticApmAgent.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/ElasticApmAgent.java index 20cabb3621..593e04f97b 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/ElasticApmAgent.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/ElasticApmAgent.java @@ -50,6 +50,7 @@ import net.bytebuddy.asm.Advice; import net.bytebuddy.asm.AsmVisitorWrapper; import net.bytebuddy.description.NamedElement; +import net.bytebuddy.description.annotation.AnnotationDescription; import net.bytebuddy.description.field.FieldDescription; import net.bytebuddy.description.field.FieldList; import net.bytebuddy.description.method.MethodDescription; @@ -61,6 +62,7 @@ import net.bytebuddy.dynamic.scaffold.TypeValidation; import net.bytebuddy.implementation.Implementation; import net.bytebuddy.matcher.ElementMatcher; +import net.bytebuddy.matcher.ElementMatchers; import net.bytebuddy.pool.TypePool; import net.bytebuddy.utility.JavaModule; import org.objectweb.asm.ClassVisitor; @@ -96,7 +98,9 @@ import static net.bytebuddy.asm.Advice.ExceptionHandler.Default.PRINTING; import static net.bytebuddy.matcher.ElementMatchers.any; import static net.bytebuddy.matcher.ElementMatchers.is; +import static net.bytebuddy.matcher.ElementMatchers.isAnnotatedWith; import static net.bytebuddy.matcher.ElementMatchers.isInterface; +import static net.bytebuddy.matcher.ElementMatchers.isStatic; import static net.bytebuddy.matcher.ElementMatchers.nameContains; import static net.bytebuddy.matcher.ElementMatchers.nameEndsWith; import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith; @@ -359,6 +363,7 @@ private static AgentBuilder.Transformer.ForAdvice getTransformer(final ElasticAp withCustomMapping = withCustomMapping.bind(offsetMapping); } if (instrumentation.indyDispatch()) { + validateAdvice(instrumentation.getAdviceClass().getName()); withCustomMapping = withCustomMapping.bootstrap(IndyBootstrap.getIndyBootstrapMethod()); } return new AgentBuilder.Transformer.ForAdvice(withCustomMapping) @@ -388,6 +393,36 @@ public boolean matches(MethodDescription target) { .withExceptionHandler(PRINTING); } + /** + * Validates invariants explained in {@link ElasticApmInstrumentation#indyDispatch()} + * @param adviceClassName the name of the advice class + */ + private static void validateAdvice(String adviceClassName) { + validateAdviceIsInlined(adviceClassName); + if (adviceClassName.startsWith("co.elastic.apm.agent.") && adviceClassName.split("\\.").length > 6) { + throw new IllegalStateException("Indy-dispatched advice class must be at the root of the instrumentation plugin."); + } + } + + private static void validateAdviceIsInlined(String adviceClassName) { + TypePool pool = new TypePool.Default(TypePool.CacheProvider.NoOp.INSTANCE, ClassFileLocator.ForClassLoader.ofSystemLoader(), TypePool.Default.ReaderMode.FAST); + TypeDescription typeDescription = pool.describe(adviceClassName).resolve(); + for (MethodDescription.InDefinedShape enterAdvice : typeDescription.getDeclaredMethods().filter(isStatic().and(isAnnotatedWith(Advice.OnMethodEnter.class)))) { + for (AnnotationDescription enter : enterAdvice.getDeclaredAnnotations().filter(ElementMatchers.annotationType(Advice.OnMethodEnter.class))) { + if (enter.prepare(Advice.OnMethodEnter.class).load().inline()) { + throw new IllegalStateException(String.format("Indy-dispatched advice %s#%s has to be declared with inline=false", adviceClassName, enterAdvice.getName())); + } + } + } + for (MethodDescription.InDefinedShape exitAdvice : typeDescription.getDeclaredMethods().filter(isStatic().and(isAnnotatedWith(Advice.OnMethodExit.class)))) { + for (AnnotationDescription exit : exitAdvice.getDeclaredAnnotations().filter(ElementMatchers.annotationType(Advice.OnMethodExit.class))) { + if (exit.prepare(Advice.OnMethodExit.class).load().inline()) { + throw new IllegalStateException(String.format("Indy-dispatched advice %s#%s has to be declared with inline=false", adviceClassName, exitAdvice.getName())); + } + } + } + } + private static MatcherTimer getOrCreateTimer(Class adviceClass) { final String name = adviceClass.getName(); MatcherTimer timer = matcherTimers.get(name); @@ -439,15 +474,26 @@ public static synchronized void reset() { if (instrumentation == null) { return; } - - if (resettableClassFileTransformer == null) { - throw new IllegalStateException("Reset was called before init"); + Exception exception = null; + if (resettableClassFileTransformer != null) { + try { + resettableClassFileTransformer.reset(instrumentation, RedefinitionStrategy.RETRANSFORMATION); + } catch (Exception e) { + exception = e; + } + resettableClassFileTransformer = null; } dynamicallyInstrumentedClasses.clear(); - resettableClassFileTransformer.reset(instrumentation, RedefinitionStrategy.RETRANSFORMATION); - resettableClassFileTransformer = null; for (ResettableClassFileTransformer transformer : dynamicClassFileTransformers) { - transformer.reset(instrumentation, RedefinitionStrategy.RETRANSFORMATION); + try { + transformer.reset(instrumentation, RedefinitionStrategy.RETRANSFORMATION); + } catch (Exception e) { + if (exception != null) { + exception.addSuppressed(e); + } else { + exception = e; + } + } } dynamicClassFileTransformers.clear(); instrumentation = null; diff --git a/apm-agent-core/src/test/java/co/elastic/apm/agent/bci/InstrumentationTest.java b/apm-agent-core/src/test/java/co/elastic/apm/agent/bci/InstrumentationTest.java index 0f364ed610..76edca4bdc 100644 --- a/apm-agent-core/src/test/java/co/elastic/apm/agent/bci/InstrumentationTest.java +++ b/apm-agent-core/src/test/java/co/elastic/apm/agent/bci/InstrumentationTest.java @@ -34,6 +34,7 @@ import co.elastic.apm.agent.impl.ElasticApmTracer; import co.elastic.apm.agent.impl.ElasticApmTracerBuilder; import co.elastic.apm.agent.matcher.WildcardMatcher; +import co.elastic.apm.agent.bci.subpackage.AdviceInSubpackageInstrumentation; import co.elastic.apm.agent.util.GlobalVariables; import net.bytebuddy.agent.ByteBuddyAgent; import net.bytebuddy.asm.Advice; @@ -48,6 +49,7 @@ import org.apache.commons.pool2.impl.CallStackUtils; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.slf4j.event.SubstituteLoggingEvent; import org.stagemonitor.configuration.ConfigurationRegistry; @@ -62,6 +64,7 @@ import static net.bytebuddy.matcher.ElementMatchers.any; import static net.bytebuddy.matcher.ElementMatchers.isConstructor; import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.none; import static net.bytebuddy.matcher.ElementMatchers.takesArguments; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; @@ -280,6 +283,7 @@ String exceptionPlease() { } @Test + @Disabled void testPatchClassFileVersionJava6ToJava7() { // loading classes compiled with bytecode level 50 (Java 6) assertThat(StringUtils.startsWithIgnoreCase("APM", "apm")).isTrue(); @@ -299,6 +303,7 @@ void testPatchClassFileVersionJava6ToJava7() { } @Test + @Disabled void testPatchClassFileVersionJava5ToJava7() { // loading classes compiled with bytecode level 49 (Java 6) new org.slf4j.event.SubstituteLoggingEvent(); @@ -318,6 +323,7 @@ void testPatchClassFileVersionJava5ToJava7() { } @Test + @Disabled void testPatchClassFileVersionJava5ToJava7CommonsMath() { org.apache.commons.math3.stat.StatUtils.max(new double[]{3.14}); @@ -336,6 +342,7 @@ void testPatchClassFileVersionJava5ToJava7CommonsMath() { } @Test + @Disabled void testPrivateConstructorJava7() { org.apache.commons.pool2.impl.CallStackUtils.newCallStack("", false, false); @@ -371,6 +378,22 @@ void testPluginClassLoaderGCdAfterUndoingInstrumentation() { await().untilAsserted(() -> assertThat(pluginClassLoader.get()).isNull()); } + @Test + void testInlinedIndyAdvice() { + assertThatThrownBy(() -> ElasticApmAgent.initInstrumentation(tracer, + ByteBuddyAgent.install(), + Collections.singletonList(new InlinedIndyAdviceInstrumentation()))) + .isInstanceOf(IllegalStateException.class); + } + + @Test + void testAdviceInSubpackage() { + assertThatThrownBy(() -> ElasticApmAgent.initInstrumentation(tracer, + ByteBuddyAgent.install(), + Collections.singletonList(new AdviceInSubpackageInstrumentation()))) + .isInstanceOf(IllegalStateException.class); + } + @Nullable public ClassLoader getPluginClassLoader() { return null; @@ -779,4 +802,31 @@ public boolean indyDispatch() { return true; } } + + public static class InlinedIndyAdviceInstrumentation extends ElasticApmInstrumentation { + + @Advice.OnMethodEnter + public static void onExit() { + } + + @Override + public ElementMatcher getTypeMatcher() { + return none(); + } + + @Override + public ElementMatcher getMethodMatcher() { + return none(); + } + + @Override + public Collection getInstrumentationGroupNames() { + return Collections.singletonList("test"); + } + + @Override + public boolean indyDispatch() { + return true; + } + } } diff --git a/apm-agent-core/src/test/java/co/elastic/apm/agent/bci/subpackage/AdviceInSubpackageInstrumentation.java b/apm-agent-core/src/test/java/co/elastic/apm/agent/bci/subpackage/AdviceInSubpackageInstrumentation.java new file mode 100644 index 0000000000..668d97679d --- /dev/null +++ b/apm-agent-core/src/test/java/co/elastic/apm/agent/bci/subpackage/AdviceInSubpackageInstrumentation.java @@ -0,0 +1,63 @@ +/*- + * #%L + * Elastic APM Java agent + * %% + * Copyright (C) 2018 - 2020 Elastic and contributors + * %% + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * #L% + */ +package co.elastic.apm.agent.bci.subpackage; + +import co.elastic.apm.agent.bci.ElasticApmInstrumentation; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; + +import java.util.Collection; +import java.util.Collections; + +import static net.bytebuddy.matcher.ElementMatchers.none; + +public class AdviceInSubpackageInstrumentation extends ElasticApmInstrumentation { + + @Advice.OnMethodEnter(inline = false) + private static void onEnter() { + } + + @Override + public ElementMatcher getTypeMatcher() { + return none(); + } + + @Override + public ElementMatcher getMethodMatcher() { + return none(); + } + + @Override + public Collection getInstrumentationGroupNames() { + return Collections.singletonList("test"); + } + + @Override + public boolean indyDispatch() { + return true; + } +} From b1648ca1bbadf42c8f4dd4e6c028e56211356248 Mon Sep 17 00:00:00 2001 From: Felix Barnsteiner Date: Sun, 14 Jun 2020 15:54:09 +0200 Subject: [PATCH 15/35] Remove inline=false from non-indy advices --- .../ApacheHttpAsyncClientInstrumentation.java | 4 +-- .../api/AbstractSpanInstrumentation.java | 34 +++++++++---------- .../api/ElasticApmApiInstrumentation.java | 10 +++--- .../api/TransactionInstrumentation.java | 8 ++--- ...asticsearchClientAsyncInstrumentation.java | 4 +-- ...lasticsearchClientSyncInstrumentation.java | 4 +-- ...asticsearchClientAsyncInstrumentation.java | 4 +-- ...lasticsearchClientSyncInstrumentation.java | 4 +-- .../kafka/KafkaProducerInstrumentation.java | 4 +-- ...onsumerRecordsIteratorInstrumentation.java | 2 +- ...sumerRecordsRecordListInstrumentation.java | 2 +- ...ConsumerRecordsRecordsInstrumentation.java | 2 +- ...rrideClassLoaderLookupInstrumentation.java | 2 +- .../OkHttp3ClientAsyncInstrumentation.java | 4 +-- .../okhttp/OkHttp3ClientInstrumentation.java | 4 +-- .../OkHttpClientAsyncInstrumentation.java | 4 +-- .../okhttp/OkHttpClientInstrumentation.java | 4 +-- .../impl/ApmScopeInstrumentation.java | 2 +- .../impl/ApmSpanBuilderInstrumentation.java | 2 +- .../impl/ApmSpanInstrumentation.java | 8 ++--- .../impl/ElasticApmTracerInstrumentation.java | 6 ++-- .../ExternalSpanContextInstrumentation.java | 8 ++--- .../impl/ScopeManagerInstrumentation.java | 6 ++-- .../impl/SpanContextInstrumentation.java | 6 ++-- 24 files changed, 69 insertions(+), 69 deletions(-) diff --git a/apm-agent-plugins/apm-apache-httpclient-plugin/src/main/java/co/elastic/apm/agent/httpclient/ApacheHttpAsyncClientInstrumentation.java b/apm-agent-plugins/apm-apache-httpclient-plugin/src/main/java/co/elastic/apm/agent/httpclient/ApacheHttpAsyncClientInstrumentation.java index 07e0a606bd..921260c3a8 100644 --- a/apm-agent-plugins/apm-apache-httpclient-plugin/src/main/java/co/elastic/apm/agent/httpclient/ApacheHttpAsyncClientInstrumentation.java +++ b/apm-agent-plugins/apm-apache-httpclient-plugin/src/main/java/co/elastic/apm/agent/httpclient/ApacheHttpAsyncClientInstrumentation.java @@ -107,7 +107,7 @@ public static class ApacheHttpAsyncClientAdvice { @AssignToArgument(index = 0, value = 0), @AssignToArgument(index = 1, value = 3) }) - @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) + @Advice.OnMethodEnter(suppress = Throwable.class) public static Object[] onBeforeExecute(@Advice.Argument(value = 0) HttpAsyncRequestProducer requestProducer, @Advice.Argument(2) HttpContext context, @Advice.Argument(value = 3) FutureCallback futureCallback) { @@ -134,7 +134,7 @@ public static Object[] onBeforeExecute(@Advice.Argument(value = 0) HttpAsyncRequ return new Object[]{requestProducer, futureCallback, wrapped, span}; } - @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class, inline = false) + @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) public static void onAfterExecute(@Advice.Enter @Nullable Object[] enter, @Advice.Thrown @Nullable Throwable t) { Span span = enter != null ? (Span) enter[3] : null; diff --git a/apm-agent-plugins/apm-api-plugin/src/main/java/co/elastic/apm/agent/plugin/api/AbstractSpanInstrumentation.java b/apm-agent-plugins/apm-api-plugin/src/main/java/co/elastic/apm/agent/plugin/api/AbstractSpanInstrumentation.java index ea70d7f527..fd54ca696a 100644 --- a/apm-agent-plugins/apm-api-plugin/src/main/java/co/elastic/apm/agent/plugin/api/AbstractSpanInstrumentation.java +++ b/apm-agent-plugins/apm-api-plugin/src/main/java/co/elastic/apm/agent/plugin/api/AbstractSpanInstrumentation.java @@ -70,7 +70,7 @@ public SetNameInstrumentation() { } @VisibleForAdvice - @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) + @Advice.OnMethodEnter(suppress = Throwable.class) public static void setName(@Advice.FieldValue(value = "span", typing = Assigner.Typing.DYNAMIC) AbstractSpan context, @Advice.Argument(0) String name) { context.withName(name, PRIO_USER_SUPPLIED); @@ -83,7 +83,7 @@ public SetTypeInstrumentation() { } @VisibleForAdvice - @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) + @Advice.OnMethodEnter(suppress = Throwable.class) public static void setType(@Advice.FieldValue(value = "span", typing = Assigner.Typing.DYNAMIC) AbstractSpan context, @Advice.Argument(0) String type) { if (context instanceof Transaction) { @@ -100,7 +100,7 @@ public SetTypesInstrumentation() { } @VisibleForAdvice - @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) + @Advice.OnMethodEnter(suppress = Throwable.class) public static void setType(@Advice.Argument(0) Object span, @Advice.Argument(1) @Nullable String type, @Advice.Argument(2) @Nullable String subtype, @@ -118,7 +118,7 @@ public DoCreateSpanInstrumentation() { @VisibleForAdvice @AssignToReturn - @Advice.OnMethodExit(suppress = Throwable.class, inline = false) + @Advice.OnMethodExit(suppress = Throwable.class) public static Span doCreateSpan(@Advice.FieldValue(value = "span", typing = Assigner.Typing.DYNAMIC) AbstractSpan context) { return context.createSpan(); } @@ -130,7 +130,7 @@ public SetStartTimestampInstrumentation() { } @VisibleForAdvice - @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) + @Advice.OnMethodEnter(suppress = Throwable.class) public static void setStartTimestamp(@Advice.FieldValue(value = "span", typing = Assigner.Typing.DYNAMIC) AbstractSpan context, @Advice.Argument(value = 0) long epochMicros) { context.setStartTimestamp(epochMicros); @@ -143,7 +143,7 @@ public EndInstrumentation() { } @VisibleForAdvice - @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) + @Advice.OnMethodEnter(suppress = Throwable.class) public static void end(@Advice.FieldValue(value = "span", typing = Assigner.Typing.DYNAMIC) AbstractSpan context) { context.end(); } @@ -155,7 +155,7 @@ public EndWithTimestampInstrumentation() { } @VisibleForAdvice - @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) + @Advice.OnMethodEnter(suppress = Throwable.class) public static void end(@Advice.FieldValue(value = "span", typing = Assigner.Typing.DYNAMIC) AbstractSpan context, @Advice.Argument(value = 0) long epochMicros) { context.end(epochMicros); @@ -175,7 +175,7 @@ public CaptureExceptionInstrumentation() { @Nullable @AssignToReturn @VisibleForAdvice - @Advice.OnMethodExit(suppress = Throwable.class, inline = false) + @Advice.OnMethodExit(suppress = Throwable.class) public static String captureException(@Advice.FieldValue(value = "span", typing = Assigner.Typing.DYNAMIC) AbstractSpan context, @Advice.Argument(0) Throwable t) { return context.captureExceptionAndGetErrorId(t); @@ -194,7 +194,7 @@ public LegacyCaptureExceptionInstrumentation() { } @VisibleForAdvice - @Advice.OnMethodExit(suppress = Throwable.class, inline = false) + @Advice.OnMethodExit(suppress = Throwable.class) public static void captureException(@Advice.FieldValue(value = "span", typing = Assigner.Typing.DYNAMIC) AbstractSpan context, @Advice.Argument(0) Throwable t) { context.captureException(t); @@ -208,7 +208,7 @@ public GetIdInstrumentation() { @AssignToReturn @VisibleForAdvice - @Advice.OnMethodExit(suppress = Throwable.class, inline = false) + @Advice.OnMethodExit(suppress = Throwable.class) public static String getId(@Advice.FieldValue(value = "span", typing = Assigner.Typing.DYNAMIC) AbstractSpan context) { return context.getTraceContext().getId().toString(); } @@ -221,7 +221,7 @@ public GetTraceIdInstrumentation() { @AssignToReturn @VisibleForAdvice - @Advice.OnMethodExit(suppress = Throwable.class, inline = false) + @Advice.OnMethodExit(suppress = Throwable.class) public static String getTraceId(@Advice.FieldValue(value = "span", typing = Assigner.Typing.DYNAMIC) AbstractSpan context) { return context.getTraceContext().getTraceId().toString(); } @@ -233,7 +233,7 @@ public AddStringLabelInstrumentation() { } @VisibleForAdvice - @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) + @Advice.OnMethodEnter(suppress = Throwable.class) public static void addLabel(@Advice.FieldValue(value = "span", typing = Assigner.Typing.DYNAMIC) AbstractSpan context, @Advice.Argument(0) String key, @Nullable @Advice.Argument(1) String value) { if (value != null) { @@ -248,7 +248,7 @@ public AddNumberLabelInstrumentation() { } @VisibleForAdvice - @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) + @Advice.OnMethodEnter(suppress = Throwable.class) public static void addLabel(@Advice.FieldValue(value = "span", typing = Assigner.Typing.DYNAMIC) AbstractSpan context, @Advice.Argument(0) String key, @Nullable @Advice.Argument(1) Number value) { if (value != null) { @@ -263,7 +263,7 @@ public AddBooleanLabelInstrumentation() { } @VisibleForAdvice - @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) + @Advice.OnMethodEnter(suppress = Throwable.class) public static void addLabel(@Advice.FieldValue(value = "span", typing = Assigner.Typing.DYNAMIC) AbstractSpan context, @Advice.Argument(0) String key, @Nullable @Advice.Argument(1) Boolean value) { if (value != null) { @@ -278,7 +278,7 @@ public ActivateInstrumentation() { } @VisibleForAdvice - @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) + @Advice.OnMethodEnter(suppress = Throwable.class) public static void activate(@Advice.FieldValue(value = "span", typing = Assigner.Typing.DYNAMIC) AbstractSpan context) { context.activate(); } @@ -291,7 +291,7 @@ public IsSampledInstrumentation() { @AssignToReturn @VisibleForAdvice - @Advice.OnMethodExit(suppress = Throwable.class, inline = false) + @Advice.OnMethodExit(suppress = Throwable.class) public static boolean isSampled(@Advice.FieldValue(value = "span", typing = Assigner.Typing.DYNAMIC) AbstractSpan context) { return context.isSampled(); } @@ -304,7 +304,7 @@ public InjectTraceHeadersInstrumentation() { } @VisibleForAdvice - @Advice.OnMethodExit(suppress = Throwable.class, inline = false) + @Advice.OnMethodExit(suppress = Throwable.class) public static void injectTraceHeaders(@Advice.FieldValue(value = "span", typing = Assigner.Typing.DYNAMIC) AbstractSpan context, @Advice.Argument(0) MethodHandle addHeaderMethodHandle, @Advice.Argument(1) @Nullable Object headerInjector) throws Throwable { diff --git a/apm-agent-plugins/apm-api-plugin/src/main/java/co/elastic/apm/agent/plugin/api/ElasticApmApiInstrumentation.java b/apm-agent-plugins/apm-api-plugin/src/main/java/co/elastic/apm/agent/plugin/api/ElasticApmApiInstrumentation.java index aaa32d5830..51f32a4093 100644 --- a/apm-agent-plugins/apm-api-plugin/src/main/java/co/elastic/apm/agent/plugin/api/ElasticApmApiInstrumentation.java +++ b/apm-agent-plugins/apm-api-plugin/src/main/java/co/elastic/apm/agent/plugin/api/ElasticApmApiInstrumentation.java @@ -67,7 +67,7 @@ public StartTransactionInstrumentation() { @Nullable @AssignToReturn @VisibleForAdvice - @Advice.OnMethodExit(suppress = Throwable.class, inline = false) + @Advice.OnMethodExit(suppress = Throwable.class) public static Object doStartTransaction(@Advice.Origin Class clazz) { if (tracer != null) { return tracer.startRootTransaction(clazz.getClassLoader()); @@ -86,7 +86,7 @@ public StartTransactionWithRemoteParentInstrumentation() { @AssignToReturn @SuppressWarnings({"UnusedAssignment", "ParameterCanBeLocal", "unused"}) @VisibleForAdvice - @Advice.OnMethodExit(suppress = Throwable.class, inline = false) + @Advice.OnMethodExit(suppress = Throwable.class) public static Transaction doStartTransaction(@Advice.Origin Class clazz, @Advice.Argument(0) MethodHandle getFirstHeader, @Advice.Argument(1) @Nullable Object headerExtractor, @@ -115,7 +115,7 @@ public CurrentTransactionInstrumentation() { @Nullable @AssignToReturn @VisibleForAdvice - @Advice.OnMethodExit(suppress = Throwable.class, inline = false) + @Advice.OnMethodExit(suppress = Throwable.class) public static Object doGetCurrentTransaction() { if (tracer == null) { return null; @@ -132,7 +132,7 @@ public CurrentSpanInstrumentation() { @Nullable @AssignToReturn @VisibleForAdvice - @Advice.OnMethodExit(suppress = Throwable.class, inline = false) + @Advice.OnMethodExit(suppress = Throwable.class) public static Object doGetCurrentSpan() { if (tracer == null) { return null; @@ -147,7 +147,7 @@ public CaptureExceptionInstrumentation() { } @VisibleForAdvice - @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) + @Advice.OnMethodEnter(suppress = Throwable.class) public static void captureException(@Advice.Origin Class clazz, @Advice.Argument(0) @Nullable Throwable e) { if (tracer != null) { tracer.captureAndReportException(e, clazz.getClassLoader()); diff --git a/apm-agent-plugins/apm-api-plugin/src/main/java/co/elastic/apm/agent/plugin/api/TransactionInstrumentation.java b/apm-agent-plugins/apm-api-plugin/src/main/java/co/elastic/apm/agent/plugin/api/TransactionInstrumentation.java index 11ed9a19cb..31972fd995 100644 --- a/apm-agent-plugins/apm-api-plugin/src/main/java/co/elastic/apm/agent/plugin/api/TransactionInstrumentation.java +++ b/apm-agent-plugins/apm-api-plugin/src/main/java/co/elastic/apm/agent/plugin/api/TransactionInstrumentation.java @@ -65,7 +65,7 @@ public SetUserInstrumentation() { } @VisibleForAdvice - @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) + @Advice.OnMethodEnter(suppress = Throwable.class) public static void setUser(@Advice.FieldValue(value = "span", typing = Assigner.Typing.DYNAMIC) Transaction transaction, @Advice.Argument(0) String id, @Advice.Argument(1) String email, @Advice.Argument(2) String username) { transaction.setUser(id, email, username); @@ -80,7 +80,7 @@ public EnsureParentIdInstrumentation() { @Nullable @AssignToReturn @VisibleForAdvice - @Advice.OnMethodExit(suppress = Throwable.class, inline = false) + @Advice.OnMethodExit(suppress = Throwable.class) public static String ensureParentId(@Advice.FieldValue(value = "span", typing = Assigner.Typing.DYNAMIC) Transaction transaction) { if (tracer == null) { return null; @@ -98,7 +98,7 @@ public SetResultInstrumentation() { super(named("setResult")); } - @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) + @Advice.OnMethodEnter(suppress = Throwable.class) public static void ensureParentId(@Advice.FieldValue(value = "span", typing = Assigner.Typing.DYNAMIC) Transaction transaction, @Advice.Argument(0) String result) { transaction.withResult(result); @@ -111,7 +111,7 @@ public AddCustomContextInstrumentation() { } @VisibleForAdvice - @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) + @Advice.OnMethodEnter(suppress = Throwable.class) public static void addCustomContext(@Advice.FieldValue(value = "span", typing = Assigner.Typing.DYNAMIC) Transaction transaction, @Advice.Argument(0) String key, @Nullable @Advice.Argument(1) Object value) { if (value != null ) { diff --git a/apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-5_6/src/main/java/co/elastic/apm/agent/es/restclient/v5_6/ElasticsearchClientAsyncInstrumentation.java b/apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-5_6/src/main/java/co/elastic/apm/agent/es/restclient/v5_6/ElasticsearchClientAsyncInstrumentation.java index 1b9988cb41..93d9a1c57b 100644 --- a/apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-5_6/src/main/java/co/elastic/apm/agent/es/restclient/v5_6/ElasticsearchClientAsyncInstrumentation.java +++ b/apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-5_6/src/main/java/co/elastic/apm/agent/es/restclient/v5_6/ElasticsearchClientAsyncInstrumentation.java @@ -78,7 +78,7 @@ public ElementMatcher getMethodMatcher() { public static class ElasticsearchRestClientAsyncAdvice { private static final RemoveOnGetThreadLocal spanTls = RemoveOnGetThreadLocal.get(ElasticsearchRestClientAsyncAdvice.class, "spanTls"); @AssignToArgument(5) - @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) + @Advice.OnMethodEnter(suppress = Throwable.class) public static ResponseListener onBeforeExecute(@Advice.Argument(0) String method, @Advice.Argument(1) String endpoint, @Advice.Argument(3) @Nullable HttpEntity entity, @@ -95,7 +95,7 @@ public static ResponseListener onBeforeExecute(@Advice.Argument(0) String method return responseListener; } - @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class, inline = false) + @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) public static void onAfterExecute(@Advice.Thrown @Nullable Throwable t) { final Span span = spanTls.getAndRemove(); if (span != null) { diff --git a/apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-5_6/src/main/java/co/elastic/apm/agent/es/restclient/v5_6/ElasticsearchClientSyncInstrumentation.java b/apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-5_6/src/main/java/co/elastic/apm/agent/es/restclient/v5_6/ElasticsearchClientSyncInstrumentation.java index 231ed3ead2..c92322fc38 100644 --- a/apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-5_6/src/main/java/co/elastic/apm/agent/es/restclient/v5_6/ElasticsearchClientSyncInstrumentation.java +++ b/apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-5_6/src/main/java/co/elastic/apm/agent/es/restclient/v5_6/ElasticsearchClientSyncInstrumentation.java @@ -52,7 +52,7 @@ public ElasticsearchClientSyncInstrumentation(ElasticApmTracer tracer) { public static class ElasticsearchRestClientAdvice { @Nullable - @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) + @Advice.OnMethodEnter(suppress = Throwable.class) public static Span onBeforeExecute(@Advice.Argument(0) String method, @Advice.Argument(1) String endpoint, @Advice.Argument(3) @Nullable HttpEntity entity) { @@ -64,7 +64,7 @@ public static Span onBeforeExecute(@Advice.Argument(0) String method, return helper.createClientSpan(method, endpoint, entity); } - @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class, inline = false) + @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) public static void onAfterExecute(@Advice.Return @Nullable Response response, @Advice.Enter @Nullable Span span, @Advice.Thrown @Nullable Throwable t) { diff --git a/apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-6_4/src/main/java/co/elastic/apm/agent/es/restclient/v6_4/ElasticsearchClientAsyncInstrumentation.java b/apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-6_4/src/main/java/co/elastic/apm/agent/es/restclient/v6_4/ElasticsearchClientAsyncInstrumentation.java index 12293448ab..a4da26d880 100644 --- a/apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-6_4/src/main/java/co/elastic/apm/agent/es/restclient/v6_4/ElasticsearchClientAsyncInstrumentation.java +++ b/apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-6_4/src/main/java/co/elastic/apm/agent/es/restclient/v6_4/ElasticsearchClientAsyncInstrumentation.java @@ -73,7 +73,7 @@ public static class ElasticsearchRestClientAsyncAdvice { private static final RemoveOnGetThreadLocal spanTls = RemoveOnGetThreadLocal.get(ElasticsearchRestClientAsyncAdvice.class, "spanTls"); @AssignToArgument(1) - @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) + @Advice.OnMethodEnter(suppress = Throwable.class) public static ResponseListener onBeforeExecute(@Advice.Argument(0) Request request, @Advice.Argument(1) ResponseListener responseListener) { ElasticsearchRestClientInstrumentationHelper helper = esClientInstrHelperManager.getForClassLoaderOfClass(Request.class); @@ -87,7 +87,7 @@ public static ResponseListener onBeforeExecute(@Advice.Argument(0) Request reque return responseListener; } - @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class, inline = false) + @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) public static void onAfterExecute(@Advice.Thrown @Nullable Throwable t) { final Span span = spanTls.getAndRemove(); if (span != null) { diff --git a/apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-6_4/src/main/java/co/elastic/apm/agent/es/restclient/v6_4/ElasticsearchClientSyncInstrumentation.java b/apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-6_4/src/main/java/co/elastic/apm/agent/es/restclient/v6_4/ElasticsearchClientSyncInstrumentation.java index 9690336839..acf70bd3a6 100644 --- a/apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-6_4/src/main/java/co/elastic/apm/agent/es/restclient/v6_4/ElasticsearchClientSyncInstrumentation.java +++ b/apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-6_4/src/main/java/co/elastic/apm/agent/es/restclient/v6_4/ElasticsearchClientSyncInstrumentation.java @@ -68,7 +68,7 @@ public ElementMatcher getMethodMatcher() { public static class ElasticsearchRestClientSyncAdvice { @Nullable - @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) + @Advice.OnMethodEnter(suppress = Throwable.class) public static Span onBeforeExecute(@Advice.Argument(0) Request request) { ElasticsearchRestClientInstrumentationHelper helper = @@ -79,7 +79,7 @@ public static Span onBeforeExecute(@Advice.Argument(0) Request request) { return helper.createClientSpan(request.getMethod(), request.getEndpoint(), request.getEntity()); } - @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class, inline = false) + @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) public static void onAfterExecute(@Advice.Return @Nullable Response response, @Advice.Enter @Nullable Span span, @Advice.Thrown @Nullable Throwable t) { diff --git a/apm-agent-plugins/apm-kafka-plugin/apm-kafka-base-plugin/src/main/java/co/elastic/apm/agent/kafka/KafkaProducerInstrumentation.java b/apm-agent-plugins/apm-kafka-plugin/apm-kafka-base-plugin/src/main/java/co/elastic/apm/agent/kafka/KafkaProducerInstrumentation.java index d71f2d460a..51d854f5de 100644 --- a/apm-agent-plugins/apm-kafka-plugin/apm-kafka-base-plugin/src/main/java/co/elastic/apm/agent/kafka/KafkaProducerInstrumentation.java +++ b/apm-agent-plugins/apm-kafka-plugin/apm-kafka-base-plugin/src/main/java/co/elastic/apm/agent/kafka/KafkaProducerInstrumentation.java @@ -76,7 +76,7 @@ public Class getAdviceClass() { public static class KafkaProducerAdvice { @Nullable @AssignToArgument(1) - @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) + @Advice.OnMethodEnter(suppress = Throwable.class) public static Callback beforeSend(@Advice.Argument(0) final ProducerRecord record, @Advice.Argument(1) @Nullable Callback callback) { if (tracer == null) { @@ -97,7 +97,7 @@ public static Callback beforeSend(@Advice.Argument(0) final ProducerRecord recor return helper.wrapCallback(callback, span); } - @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class, inline = false) + @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) public static void afterSend(@Advice.Argument(0) final ProducerRecord record, @Advice.This final KafkaProducer thiz, @Advice.Thrown final Throwable throwable) { diff --git a/apm-agent-plugins/apm-kafka-plugin/apm-kafka-headers-plugin/src/main/java/co/elastic/apm/agent/kafka/ConsumerRecordsIteratorInstrumentation.java b/apm-agent-plugins/apm-kafka-plugin/apm-kafka-headers-plugin/src/main/java/co/elastic/apm/agent/kafka/ConsumerRecordsIteratorInstrumentation.java index f8b8056d25..a9fc39d4c3 100644 --- a/apm-agent-plugins/apm-kafka-plugin/apm-kafka-headers-plugin/src/main/java/co/elastic/apm/agent/kafka/ConsumerRecordsIteratorInstrumentation.java +++ b/apm-agent-plugins/apm-kafka-plugin/apm-kafka-headers-plugin/src/main/java/co/elastic/apm/agent/kafka/ConsumerRecordsIteratorInstrumentation.java @@ -69,7 +69,7 @@ public static class ConsumerRecordsAdvice { @Nullable @AssignToReturn - @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class, inline = false) + @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) public static Iterator wrapIterator(@Nullable @Advice.Return Iterator iterator) { if (tracer == null || !tracer.isRunning() || tracer.currentTransaction() != null) { return iterator; diff --git a/apm-agent-plugins/apm-kafka-plugin/apm-kafka-headers-plugin/src/main/java/co/elastic/apm/agent/kafka/ConsumerRecordsRecordListInstrumentation.java b/apm-agent-plugins/apm-kafka-plugin/apm-kafka-headers-plugin/src/main/java/co/elastic/apm/agent/kafka/ConsumerRecordsRecordListInstrumentation.java index 003f84d623..6ab5f9d312 100644 --- a/apm-agent-plugins/apm-kafka-plugin/apm-kafka-headers-plugin/src/main/java/co/elastic/apm/agent/kafka/ConsumerRecordsRecordListInstrumentation.java +++ b/apm-agent-plugins/apm-kafka-plugin/apm-kafka-headers-plugin/src/main/java/co/elastic/apm/agent/kafka/ConsumerRecordsRecordListInstrumentation.java @@ -70,7 +70,7 @@ public static class ConsumerRecordsAdvice { @Nullable @AssignToReturn - @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class, inline = false) + @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) public static List wrapRecordList(@Nullable @Advice.Return List list) { if (tracer == null || !tracer.isRunning() || tracer.currentTransaction() != null) { return list; diff --git a/apm-agent-plugins/apm-kafka-plugin/apm-kafka-headers-plugin/src/main/java/co/elastic/apm/agent/kafka/ConsumerRecordsRecordsInstrumentation.java b/apm-agent-plugins/apm-kafka-plugin/apm-kafka-headers-plugin/src/main/java/co/elastic/apm/agent/kafka/ConsumerRecordsRecordsInstrumentation.java index 940cdcad1c..f0412b7549 100644 --- a/apm-agent-plugins/apm-kafka-plugin/apm-kafka-headers-plugin/src/main/java/co/elastic/apm/agent/kafka/ConsumerRecordsRecordsInstrumentation.java +++ b/apm-agent-plugins/apm-kafka-plugin/apm-kafka-headers-plugin/src/main/java/co/elastic/apm/agent/kafka/ConsumerRecordsRecordsInstrumentation.java @@ -68,7 +68,7 @@ public static class ConsumerRecordsAdvice { @Nullable @AssignToReturn - @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class, inline = false) + @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) public static Iterable wrapIterable(@Nullable @Advice.Return Iterable iterable) { if (tracer == null || !tracer.isRunning() || tracer.currentTransaction() != null) { return iterable; diff --git a/apm-agent-plugins/apm-mule4-plugin/src/main/java/co/elastic/apm/agent/mule4/Mule4OverrideClassLoaderLookupInstrumentation.java b/apm-agent-plugins/apm-mule4-plugin/src/main/java/co/elastic/apm/agent/mule4/Mule4OverrideClassLoaderLookupInstrumentation.java index 3af4d76037..7f3ec8dc8f 100644 --- a/apm-agent-plugins/apm-mule4-plugin/src/main/java/co/elastic/apm/agent/mule4/Mule4OverrideClassLoaderLookupInstrumentation.java +++ b/apm-agent-plugins/apm-mule4-plugin/src/main/java/co/elastic/apm/agent/mule4/Mule4OverrideClassLoaderLookupInstrumentation.java @@ -79,7 +79,7 @@ public Class getAdviceClass() { public static class Mule4OverrideClassLoaderLookupAdvice { @AssignToReturn - @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class, inline = false) + @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) public static LookupStrategy makeParentOnlyForAgentClasses(@Advice.Argument(0) @Nullable final String packageName, @Advice.Return LookupStrategy lookupStrategy) { diff --git a/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttp3ClientAsyncInstrumentation.java b/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttp3ClientAsyncInstrumentation.java index a56cbb0470..048de64f1a 100644 --- a/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttp3ClientAsyncInstrumentation.java +++ b/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttp3ClientAsyncInstrumentation.java @@ -86,7 +86,7 @@ public static class OkHttpClient3ExecuteAdvice { fields = @AssignToField(index = 0, value = "originalRequest"), arguments = @AssignToArgument(index = 1, value = 0) ) - @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) + @Advice.OnMethodEnter(suppress = Throwable.class) public static Object[] onBeforeEnqueue(@Advice.Origin Class clazz, @Advice.FieldValue("originalRequest") @Nullable okhttp3.Request originalRequest, @Advice.Argument(0) @Nullable Callback callback) { @@ -120,7 +120,7 @@ public static Object[] onBeforeEnqueue(@Advice.Origin Class claz return new Object[] {originalRequest, callback, span}; } - @Advice.OnMethodExit(suppress = Throwable.class, inline = false) + @Advice.OnMethodExit(suppress = Throwable.class) public static void onAfterEnqueue(@Advice.Enter @Nullable Object[] enter) { Span span = enter != null ? (Span) enter[2] : null; if (span != null) { diff --git a/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttp3ClientInstrumentation.java b/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttp3ClientInstrumentation.java index 4ded66d88c..deed505ef7 100644 --- a/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttp3ClientInstrumentation.java +++ b/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttp3ClientInstrumentation.java @@ -63,7 +63,7 @@ public static class OkHttpClient3ExecuteAdvice { @Nullable @AssignToField(value = "originalRequest", typing = Assigner.Typing.DYNAMIC) - @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) + @Advice.OnMethodEnter(suppress = Throwable.class) public static Object onBeforeExecute(@Advice.FieldValue("originalRequest") @Nullable Object originalRequest) { if (tracer == null || tracer.getActive() == null || !(originalRequest instanceof Request)) { @@ -91,7 +91,7 @@ public static Object onBeforeExecute(@Advice.FieldValue("originalRequest") @Null return originalRequest; } - @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class, inline = false) + @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) public static void onAfterExecute(@Advice.Return @Nullable okhttp3.Response response, @Advice.Thrown @Nullable Throwable t) { final Span span = spanTls.getAndRemove(); diff --git a/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttpClientAsyncInstrumentation.java b/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttpClientAsyncInstrumentation.java index 710163eb89..3c7553644b 100644 --- a/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttpClientAsyncInstrumentation.java +++ b/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttpClientAsyncInstrumentation.java @@ -84,7 +84,7 @@ public static class OkHttpClient3ExecuteAdvice { fields = @AssignToField(index = 0, value = "originalRequest"), arguments = @AssignToArgument(index = 1, value = 0) ) - @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) + @Advice.OnMethodEnter(suppress = Throwable.class) public static Object[] onBeforeEnqueue(@Advice.Origin Class clazz, @Advice.FieldValue("originalRequest") @Nullable Request originalRequest, @Advice.Argument(0) @Nullable Callback callback) { @@ -118,7 +118,7 @@ public static Object[] onBeforeEnqueue(@Advice.Origin Class claz return new Object[] {originalRequest, callback, span}; } - @Advice.OnMethodExit(suppress = Throwable.class, inline = false) + @Advice.OnMethodExit(suppress = Throwable.class) public static void onAfterEnqueue(@Advice.Enter @Nullable Object[] enter) { Span span = enter != null ? (Span) enter[2] : null; if (span != null) { diff --git a/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttpClientInstrumentation.java b/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttpClientInstrumentation.java index 3a7f38ee41..9a5db14075 100644 --- a/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttpClientInstrumentation.java +++ b/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttpClientInstrumentation.java @@ -62,7 +62,7 @@ public static class OkHttpClientExecuteAdvice { @Nullable @AssignToField(value = "originalRequest", typing = Assigner.Typing.DYNAMIC) - @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) + @Advice.OnMethodEnter(suppress = Throwable.class) public static Object onBeforeExecute(@Advice.FieldValue("originalRequest") @Nullable Object originalRequest) { if (tracer == null || tracer.getActive() == null || !(originalRequest instanceof com.squareup.okhttp.Request)) { @@ -90,7 +90,7 @@ public static Object onBeforeExecute(@Advice.FieldValue("originalRequest") @Null return originalRequest; } - @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class, inline = false) + @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) public static void onAfterExecute(@Advice.Return @Nullable com.squareup.okhttp.Response response, @Advice.Thrown @Nullable Throwable t) { Span span = spanTls.getAndRemove(); diff --git a/apm-agent-plugins/apm-opentracing-plugin/src/main/java/co/elastic/apm/agent/opentracing/impl/ApmScopeInstrumentation.java b/apm-agent-plugins/apm-opentracing-plugin/src/main/java/co/elastic/apm/agent/opentracing/impl/ApmScopeInstrumentation.java index 168188a967..e19d5b1985 100644 --- a/apm-agent-plugins/apm-opentracing-plugin/src/main/java/co/elastic/apm/agent/opentracing/impl/ApmScopeInstrumentation.java +++ b/apm-agent-plugins/apm-opentracing-plugin/src/main/java/co/elastic/apm/agent/opentracing/impl/ApmScopeInstrumentation.java @@ -39,7 +39,7 @@ public class ApmScopeInstrumentation extends OpenTracingBridgeInstrumentation { @VisibleForAdvice - @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) + @Advice.OnMethodEnter(suppress = Throwable.class) public static void release(@Advice.Argument(value = 0, typing = Assigner.Typing.DYNAMIC) @Nullable AbstractSpan dispatcher) { if (dispatcher != null) { dispatcher.deactivate(); diff --git a/apm-agent-plugins/apm-opentracing-plugin/src/main/java/co/elastic/apm/agent/opentracing/impl/ApmSpanBuilderInstrumentation.java b/apm-agent-plugins/apm-opentracing-plugin/src/main/java/co/elastic/apm/agent/opentracing/impl/ApmSpanBuilderInstrumentation.java index 03c7f9f0ff..8c1953f51a 100644 --- a/apm-agent-plugins/apm-opentracing-plugin/src/main/java/co/elastic/apm/agent/opentracing/impl/ApmSpanBuilderInstrumentation.java +++ b/apm-agent-plugins/apm-opentracing-plugin/src/main/java/co/elastic/apm/agent/opentracing/impl/ApmSpanBuilderInstrumentation.java @@ -72,7 +72,7 @@ public CreateSpanInstrumentation() { @Nullable @AssignToReturn - @Advice.OnMethodExit(suppress = Throwable.class, inline = false) + @Advice.OnMethodExit(suppress = Throwable.class) public static Object createSpan(@Advice.Argument(value = 0, typing = Assigner.Typing.DYNAMIC) @Nullable AbstractSpan parentContext, @Advice.Origin Class spanBuilderClass, @Advice.FieldValue(value = "tags") Map tags, diff --git a/apm-agent-plugins/apm-opentracing-plugin/src/main/java/co/elastic/apm/agent/opentracing/impl/ApmSpanInstrumentation.java b/apm-agent-plugins/apm-opentracing-plugin/src/main/java/co/elastic/apm/agent/opentracing/impl/ApmSpanInstrumentation.java index 22d716cc77..26c5b61e5c 100644 --- a/apm-agent-plugins/apm-opentracing-plugin/src/main/java/co/elastic/apm/agent/opentracing/impl/ApmSpanInstrumentation.java +++ b/apm-agent-plugins/apm-opentracing-plugin/src/main/java/co/elastic/apm/agent/opentracing/impl/ApmSpanInstrumentation.java @@ -112,7 +112,7 @@ public SetOperationName() { } @VisibleForAdvice - @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) + @Advice.OnMethodEnter(suppress = Throwable.class) public static void setOperationName(@Advice.FieldValue(value = "dispatcher", typing = Assigner.Typing.DYNAMIC) @Nullable AbstractSpan span, @Advice.Argument(0) @Nullable String operationName) { if (span != null) { @@ -129,7 +129,7 @@ public LogInstrumentation() { } @VisibleForAdvice - @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) + @Advice.OnMethodEnter(suppress = Throwable.class) public static void log(@Advice.FieldValue(value = "dispatcher", typing = Assigner.Typing.DYNAMIC) @Nullable AbstractSpan span, @Advice.Argument(0) long epochTimestampMicros, @Advice.Argument(1) Map fields) { @@ -158,7 +158,7 @@ public TagInstrumentation() { } @VisibleForAdvice - @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) + @Advice.OnMethodEnter(suppress = Throwable.class) public static void handleTag(@Advice.FieldValue(value = "dispatcher", typing = Assigner.Typing.DYNAMIC) @Nullable AbstractSpan span, @Advice.Argument(0) String key, @Advice.Argument(1) @Nullable Object value) { @@ -293,7 +293,7 @@ public GetTraceContextInstrumentation() { @Nullable @AssignToReturn - @Advice.OnMethodExit(suppress = Throwable.class, inline = false) + @Advice.OnMethodExit(suppress = Throwable.class) public static Object getTraceContext(@Advice.Argument(value = 0, typing = Assigner.Typing.DYNAMIC) @Nullable AbstractSpan abstractSpan) { return abstractSpan; } diff --git a/apm-agent-plugins/apm-opentracing-plugin/src/main/java/co/elastic/apm/agent/opentracing/impl/ElasticApmTracerInstrumentation.java b/apm-agent-plugins/apm-opentracing-plugin/src/main/java/co/elastic/apm/agent/opentracing/impl/ElasticApmTracerInstrumentation.java index 52aab3cbf7..b4020acf2e 100644 --- a/apm-agent-plugins/apm-opentracing-plugin/src/main/java/co/elastic/apm/agent/opentracing/impl/ElasticApmTracerInstrumentation.java +++ b/apm-agent-plugins/apm-opentracing-plugin/src/main/java/co/elastic/apm/agent/opentracing/impl/ElasticApmTracerInstrumentation.java @@ -11,9 +11,9 @@ * the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -33,7 +33,7 @@ public class ElasticApmTracerInstrumentation extends OpenTracingBridgeInstrumentation { - @Advice.OnMethodExit(suppress = Throwable.class, inline = false) + @Advice.OnMethodExit(suppress = Throwable.class) public static void close() { if (tracer != null) { tracer.stop(); diff --git a/apm-agent-plugins/apm-opentracing-plugin/src/main/java/co/elastic/apm/agent/opentracing/impl/ExternalSpanContextInstrumentation.java b/apm-agent-plugins/apm-opentracing-plugin/src/main/java/co/elastic/apm/agent/opentracing/impl/ExternalSpanContextInstrumentation.java index 2f97fb2b19..221e05e4e1 100644 --- a/apm-agent-plugins/apm-opentracing-plugin/src/main/java/co/elastic/apm/agent/opentracing/impl/ExternalSpanContextInstrumentation.java +++ b/apm-agent-plugins/apm-opentracing-plugin/src/main/java/co/elastic/apm/agent/opentracing/impl/ExternalSpanContextInstrumentation.java @@ -69,7 +69,7 @@ public ToTraceIdInstrumentation() { @Nullable @AssignToField(value = "childTraceContext") - @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) + @Advice.OnMethodEnter(suppress = Throwable.class) public static TraceContext toTraceId(@Advice.FieldValue(value = "textMap", typing = Assigner.Typing.DYNAMIC) @Nullable Iterable> textMap, @Advice.FieldValue(value = "childTraceContext", typing = Assigner.Typing.DYNAMIC) @Nullable TraceContext childTraceContext) { if (textMap == null) { @@ -82,7 +82,7 @@ public static TraceContext toTraceId(@Advice.FieldValue(value = "textMap", typin @Nullable @AssignToReturn - @Advice.OnMethodExit(suppress = Throwable.class, inline = false) + @Advice.OnMethodExit(suppress = Throwable.class) public static String onExit(@Advice.FieldValue(value = "childTraceContext", typing = Assigner.Typing.DYNAMIC) @Nullable TraceContext childTraceContext) { if (childTraceContext == null) { return null; @@ -99,7 +99,7 @@ public ToSpanIdInstrumentation() { @Nullable @AssignToField(value = "childTraceContext") - @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) + @Advice.OnMethodEnter(suppress = Throwable.class) public static TraceContext toSpanId(@Advice.FieldValue(value = "textMap", typing = Assigner.Typing.DYNAMIC) @Nullable Iterable> textMap, @Advice.FieldValue(value = "childTraceContext", typing = Assigner.Typing.DYNAMIC) @Nullable TraceContext childTraceContext) { if (textMap == null) { @@ -110,7 +110,7 @@ public static TraceContext toSpanId(@Advice.FieldValue(value = "textMap", typing @Nullable @AssignToReturn - @Advice.OnMethodExit(suppress = Throwable.class, inline = false) + @Advice.OnMethodExit(suppress = Throwable.class) public static String onExit(@Advice.FieldValue(value = "childTraceContext", typing = Assigner.Typing.DYNAMIC) @Nullable TraceContext childTraceContext) { if (childTraceContext == null) { return null; diff --git a/apm-agent-plugins/apm-opentracing-plugin/src/main/java/co/elastic/apm/agent/opentracing/impl/ScopeManagerInstrumentation.java b/apm-agent-plugins/apm-opentracing-plugin/src/main/java/co/elastic/apm/agent/opentracing/impl/ScopeManagerInstrumentation.java index 60dcea73ab..0136c0f169 100644 --- a/apm-agent-plugins/apm-opentracing-plugin/src/main/java/co/elastic/apm/agent/opentracing/impl/ScopeManagerInstrumentation.java +++ b/apm-agent-plugins/apm-opentracing-plugin/src/main/java/co/elastic/apm/agent/opentracing/impl/ScopeManagerInstrumentation.java @@ -62,7 +62,7 @@ public ActivateInstrumentation() { } @VisibleForAdvice - @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) + @Advice.OnMethodEnter(suppress = Throwable.class) public static void doActivate(@Advice.Argument(value = 0, typing = Assigner.Typing.DYNAMIC) @Nullable AbstractSpan span) { if (span != null) { span.activate(); @@ -79,7 +79,7 @@ public CurrentSpanInstrumentation() { @Nullable @AssignToReturn @VisibleForAdvice - @Advice.OnMethodExit(suppress = Throwable.class, inline = false) + @Advice.OnMethodExit(suppress = Throwable.class) public static Object getCurrentSpan() { if (tracer == null) { return null; @@ -98,7 +98,7 @@ public CurrentTraceContextInstrumentation() { @Nullable @AssignToReturn @VisibleForAdvice - @Advice.OnMethodExit(suppress = Throwable.class, inline = false) + @Advice.OnMethodExit(suppress = Throwable.class) public static Object getCurrentTraceContext() { if (tracer == null) { return null; diff --git a/apm-agent-plugins/apm-opentracing-plugin/src/main/java/co/elastic/apm/agent/opentracing/impl/SpanContextInstrumentation.java b/apm-agent-plugins/apm-opentracing-plugin/src/main/java/co/elastic/apm/agent/opentracing/impl/SpanContextInstrumentation.java index 6c3537e81b..98cce76aa1 100644 --- a/apm-agent-plugins/apm-opentracing-plugin/src/main/java/co/elastic/apm/agent/opentracing/impl/SpanContextInstrumentation.java +++ b/apm-agent-plugins/apm-opentracing-plugin/src/main/java/co/elastic/apm/agent/opentracing/impl/SpanContextInstrumentation.java @@ -72,7 +72,7 @@ public BaggageItemsInstrumentation() { @Nullable @AssignToReturn - @Advice.OnMethodExit(suppress = Throwable.class, inline = false) + @Advice.OnMethodExit(suppress = Throwable.class) public static Iterable> baggageItems(@Advice.FieldValue(value = "traceContext", typing = Assigner.Typing.DYNAMIC) @Nullable AbstractSpan traceContext) { if (traceContext != null) { return doGetBaggage(traceContext); @@ -98,7 +98,7 @@ public ToTraceIdInstrumentation() { @Nullable @AssignToReturn - @Advice.OnMethodExit(suppress = Throwable.class, inline = false) + @Advice.OnMethodExit(suppress = Throwable.class) public static String toTraceId(@Advice.FieldValue(value = "traceContext", typing = Assigner.Typing.DYNAMIC) @Nullable AbstractSpan traceContext) { if (traceContext == null) { return null; @@ -115,7 +115,7 @@ public ToSpanIdInstrumentation() { @Nullable @AssignToReturn - @Advice.OnMethodExit(suppress = Throwable.class, inline = false) + @Advice.OnMethodExit(suppress = Throwable.class) public static String toTraceId(@Advice.FieldValue(value = "traceContext", typing = Assigner.Typing.DYNAMIC) @Nullable AbstractSpan traceContext) { if (traceContext == null) { return null; From 42d483914945ed6e26d0c78048ea3a78204eb906 Mon Sep 17 00:00:00 2001 From: Felix Barnsteiner Date: Sun, 14 Jun 2020 17:34:22 +0200 Subject: [PATCH 16/35] Add docs, rename indyDispatch to indyPlugin --- .../apm/agent/bci/ElasticApmAgent.java | 57 +----------- .../agent/bci/ElasticApmInstrumentation.java | 4 +- .../elastic/apm/agent/bci/IndyBootstrap.java | 6 +- .../bci/bytebuddy/ErrorLoggingListener.java | 7 -- .../PatchBytecodeVersionTo51Transformer.java | 90 +++++++++++++++++++ .../bci/bytebuddy/postprocessor/AssignTo.java | 11 ++- .../postprocessor/AssignToArgument.java | 19 +++- .../postprocessor/AssignToField.java | 9 +- .../postprocessor/AssignToReturn.java | 10 +++ .../bytebuddy/postprocessor/package-info.java | 32 ++++++- .../threadlocal/RemoveOnGetThreadLocal.java | 6 ++ .../apm/agent/bci/InstrumentationTest.java | 14 +-- .../AdviceInSubpackageInstrumentation.java | 6 +- .../apm/agent/jdbc/JdbcInstrumentation.java | 2 +- .../AbstractServletInstrumentation.java | 2 +- 15 files changed, 190 insertions(+), 85 deletions(-) create mode 100644 apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/PatchBytecodeVersionTo51Transformer.java diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/ElasticApmAgent.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/ElasticApmAgent.java index 593e04f97b..da9f520139 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/ElasticApmAgent.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/ElasticApmAgent.java @@ -29,6 +29,7 @@ import co.elastic.apm.agent.bci.bytebuddy.FailSafeDeclaredMethodsCompiler; import co.elastic.apm.agent.bci.bytebuddy.MatcherTimer; import co.elastic.apm.agent.bci.bytebuddy.MinimumClassFileVersionValidator; +import co.elastic.apm.agent.bci.bytebuddy.PatchBytecodeVersionTo51Transformer; import co.elastic.apm.agent.bci.bytebuddy.RootPackageCustomLocator; import co.elastic.apm.agent.bci.bytebuddy.SimpleMethodSignatureOffsetMappingFactory; import co.elastic.apm.agent.bci.bytebuddy.SoftlyReferencingTypePoolCache; @@ -48,27 +49,18 @@ import net.bytebuddy.agent.builder.AgentBuilder.RedefinitionStrategy; import net.bytebuddy.agent.builder.ResettableClassFileTransformer; import net.bytebuddy.asm.Advice; -import net.bytebuddy.asm.AsmVisitorWrapper; import net.bytebuddy.description.NamedElement; import net.bytebuddy.description.annotation.AnnotationDescription; -import net.bytebuddy.description.field.FieldDescription; -import net.bytebuddy.description.field.FieldList; import net.bytebuddy.description.method.MethodDescription; -import net.bytebuddy.description.method.MethodList; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.dynamic.ClassFileLocator; import net.bytebuddy.dynamic.DynamicType; import net.bytebuddy.dynamic.loading.ByteArrayClassLoader; import net.bytebuddy.dynamic.scaffold.TypeValidation; -import net.bytebuddy.implementation.Implementation; import net.bytebuddy.matcher.ElementMatcher; import net.bytebuddy.matcher.ElementMatchers; import net.bytebuddy.pool.TypePool; import net.bytebuddy.utility.JavaModule; -import org.objectweb.asm.ClassVisitor; -import org.objectweb.asm.MethodVisitor; -import org.objectweb.asm.Opcodes; -import org.objectweb.asm.commons.JSRInlinerAdapter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.stagemonitor.configuration.ConfigurationOption; @@ -107,7 +99,6 @@ import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.none; import static net.bytebuddy.matcher.ElementMatchers.not; -import static org.objectweb.asm.ClassWriter.COMPUTE_FRAMES; public class ElasticApmAgent { @@ -301,47 +292,7 @@ public boolean matches(TypeDescription typeDescription, ClassLoader classLoader, } } }) - .transform(new AgentBuilder.Transformer() { - @Override - public DynamicType.Builder transform(DynamicType.Builder builder, TypeDescription typeDescription, ClassLoader classLoader, JavaModule javaModule) { - return builder.visit(new AsmVisitorWrapper.AbstractBase() { - @Override - public ClassVisitor wrap(TypeDescription typeDescription, ClassVisitor classVisitor, Implementation.Context context, - TypePool typePool, FieldList fieldList, MethodList methodList, int writerFlags, int readerFlags) { - return new ClassVisitor(Opcodes.ASM7, classVisitor) { - private boolean patchVersion; - - @Override - public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { - if (version < Opcodes.V1_7) { - patchVersion = true; - // up-patching the class file verison to version 7 in order to support advice dispatching via invoke dynamic - version = Opcodes.V1_7; - } - super.visit(version, access, name, signature, superName, interfaces); - } - - @Override - public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { - final MethodVisitor methodVisitor = super.visitMethod(access, name, descriptor, signature, exceptions); - if (patchVersion) { - return new JSRInlinerAdapter(methodVisitor, access, name, descriptor, signature, exceptions); - } else { - return methodVisitor; - } - } - }; - } - - @Override - public int mergeWriter(int flags) { - // class files with version < Java 7 don't require a stack frame map - // as we're patching the version to at least 7, we have to compute the frames - return flags | COMPUTE_FRAMES; - } - }); - } - }) + .transform(new PatchBytecodeVersionTo51Transformer()) .transform(getTransformer(tracer, instrumentation, logger, methodMatcher)) .transform(new AgentBuilder.Transformer() { @Override @@ -362,7 +313,7 @@ private static AgentBuilder.Transformer.ForAdvice getTransformer(final ElasticAp if (offsetMapping != null) { withCustomMapping = withCustomMapping.bind(offsetMapping); } - if (instrumentation.indyDispatch()) { + if (instrumentation.indyPlugin()) { validateAdvice(instrumentation.getAdviceClass().getName()); withCustomMapping = withCustomMapping.bootstrap(IndyBootstrap.getIndyBootstrapMethod()); } @@ -394,7 +345,7 @@ public boolean matches(MethodDescription target) { } /** - * Validates invariants explained in {@link ElasticApmInstrumentation#indyDispatch()} + * Validates invariants explained in {@link ElasticApmInstrumentation#indyPlugin()} * @param adviceClassName the name of the advice class */ private static void validateAdvice(String adviceClassName) { diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/ElasticApmInstrumentation.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/ElasticApmInstrumentation.java index e657bc482c..6129e4e475 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/ElasticApmInstrumentation.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/ElasticApmInstrumentation.java @@ -205,7 +205,7 @@ public void onTypeMatch(TypeDescription typeDescription, ClassLoader classLoader * this will call {@link IndyBootstrap#bootstrap} to determine the target {@link java.lang.invoke.ConstantCallSite}. *

*

- * Things to watch out for when using indy dispatch: + * Things to watch out for when using indy plugins: *

    *
  • * When an advice instruments classes in multiple class loaders, the plugin classes will be loaded form multiple class loaders. @@ -235,7 +235,7 @@ public void onTypeMatch(TypeDescription typeDescription, ClassLoader classLoader * and dispatch to the {@linkplain #getAdviceClass() advice} via an {@code INVOKEDYNAMIC} instruction. * @see IndyBootstrap */ - public boolean indyDispatch() { + public boolean indyPlugin() { return false; } } diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/IndyBootstrap.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/IndyBootstrap.java index e94815c4f2..d2863a0dd5 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/IndyBootstrap.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/IndyBootstrap.java @@ -45,7 +45,7 @@ import static net.bytebuddy.matcher.ElementMatchers.named; /** - * When {@link ElasticApmInstrumentation#indyDispatch()} returns {@code true}, + * When {@link ElasticApmInstrumentation#indyPlugin()} returns {@code true}, * we instruct Byte Buddy (via {@link Advice.WithCustomMapping#bootstrap(java.lang.reflect.Method)}) * to dispatch {@linkplain Advice.OnMethodEnter#inline() non-inlined advices} via an invokedynamic (indy) instruction. * The target method is linked to a dynamically created plugin class loader that is specific to an instrumentation plugin @@ -140,10 +140,10 @@ *
  • *
  • * There are some things to watch out for when writing plugins, - * as explained in {@link ElasticApmInstrumentation#indyDispatch()} + * as explained in {@link ElasticApmInstrumentation#indyPlugin()} *
  • *
- * @see ElasticApmInstrumentation#indyDispatch() + * @see ElasticApmInstrumentation#indyPlugin() */ public class IndyBootstrap { diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/ErrorLoggingListener.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/ErrorLoggingListener.java index 71a3da1011..0994bd5bbf 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/ErrorLoggingListener.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/ErrorLoggingListener.java @@ -25,17 +25,10 @@ package co.elastic.apm.agent.bci.bytebuddy; import net.bytebuddy.agent.builder.AgentBuilder; -import net.bytebuddy.description.type.TypeDescription; -import net.bytebuddy.dynamic.DynamicType; import net.bytebuddy.utility.JavaModule; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; - public class ErrorLoggingListener extends AgentBuilder.Listener.Adapter { private static final Logger logger = LoggerFactory.getLogger(ErrorLoggingListener.class); diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/PatchBytecodeVersionTo51Transformer.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/PatchBytecodeVersionTo51Transformer.java new file mode 100644 index 0000000000..54b67b5870 --- /dev/null +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/PatchBytecodeVersionTo51Transformer.java @@ -0,0 +1,90 @@ +/*- + * #%L + * Elastic APM Java agent + * %% + * Copyright (C) 2018 - 2020 Elastic and contributors + * %% + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * #L% + */ +package co.elastic.apm.agent.bci.bytebuddy; + +import co.elastic.apm.agent.bci.ElasticApmInstrumentation; +import net.bytebuddy.agent.builder.AgentBuilder; +import net.bytebuddy.asm.Advice; +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.dynamic.DynamicType; +import net.bytebuddy.implementation.Implementation; +import net.bytebuddy.pool.TypePool; +import net.bytebuddy.utility.JavaModule; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.commons.JSRInlinerAdapter; + +import static org.objectweb.asm.ClassWriter.COMPUTE_FRAMES; + +/** + * Patches the class file version to 51 (Java 7) in order to support injecting {@code INVOKEDYNAMIC} instructions via + * {@link Advice.WithCustomMapping#bootstrap} which is important for {@linkplain ElasticApmInstrumentation#indyPlugin() indy plugins}. + */ +public class PatchBytecodeVersionTo51Transformer implements AgentBuilder.Transformer { + @Override + public DynamicType.Builder transform(DynamicType.Builder builder, TypeDescription typeDescription, ClassLoader classLoader, JavaModule javaModule) { + return builder.visit(new AsmVisitorWrapper.AbstractBase() { + @Override + public ClassVisitor wrap(TypeDescription typeDescription, ClassVisitor classVisitor, Implementation.Context context, + TypePool typePool, FieldList fieldList, MethodList methodList, int writerFlags, int readerFlags) { + return new ClassVisitor(Opcodes.ASM7, classVisitor) { + private boolean patchVersion; + + @Override + public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { + if (version < Opcodes.V1_7) { + patchVersion = true; + // + version = Opcodes.V1_7; + } + super.visit(version, access, name, signature, superName, interfaces); + } + + @Override + public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { + final MethodVisitor methodVisitor = super.visitMethod(access, name, descriptor, signature, exceptions); + if (patchVersion) { + return new JSRInlinerAdapter(methodVisitor, access, name, descriptor, signature, exceptions); + } else { + return methodVisitor; + } + } + }; + } + + @Override + public int mergeWriter(int flags) { + // class files with version < Java 7 don't require a stack frame map + // as we're patching the version to at least 7, we have to compute the frames + return flags | COMPUTE_FRAMES; + } + }); + } +} diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignTo.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignTo.java index 1470fbddc2..4abad800fa 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignTo.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignTo.java @@ -11,9 +11,9 @@ * the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -29,9 +29,16 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +/** + * Used to assign a single {@code Object[]} return value of an advice to multiple bindings. + */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface AssignTo { + /** + * The arguments to assign to + * @return + */ AssignToArgument[] arguments() default {}; AssignToField[] fields() default {}; AssignToReturn[] returns() default {}; diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToArgument.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToArgument.java index c6b42ba9b5..d6c458f66e 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToArgument.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToArgument.java @@ -35,9 +35,24 @@ @Target(ElementType.METHOD) public @interface AssignToArgument { + /** + * Returns the index of the mapped parameter. + * + * @return The index of the mapped parameter. + */ int value(); - int index() default -1; - + /** + * The typing that should be applied when assigning the argument. + * + * @return The typing to apply upon assignment. + */ Assigner.Typing typing() default Assigner.Typing.STATIC; + + /** + * Used in combination with {@link AssignTo} to select the index of the returned {@code Object[]} that should be used for the assignment. + * + * @return the index of the {@code Object[]} that should be used for the assignment. + */ + int index() default -1; } diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToField.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToField.java index 7cbb6e2958..ab22c7fb7e 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToField.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToField.java @@ -42,8 +42,6 @@ */ String value(); - int index() default -1; - /** * Returns the type that declares the field that should be mapped to the annotated parameter. If this property * is set to {@code void}, the field is looked up implicitly within the instrumented class's class hierarchy. @@ -60,4 +58,11 @@ * @return The typing to apply upon assignment. */ Assigner.Typing typing() default Assigner.Typing.STATIC; + + /** + * Used in combination with {@link AssignTo} to select the index of the returned {@code Object[]} that should be used for the assignment. + * + * @return the index of the {@code Object[]} that should be used for the assignment. + */ + int index() default -1; } diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToReturn.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToReturn.java index 42c26f3e3b..b1d9ab2626 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToReturn.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToReturn.java @@ -35,7 +35,17 @@ @Target(ElementType.METHOD) public @interface AssignToReturn { + /** + * Determines the typing that is applied when assigning the return value. + * + * @return The typing to apply when assigning the annotated parameter. + */ Assigner.Typing typing() default Assigner.Typing.STATIC; + /** + * Used in combination with {@link AssignTo} to select the index of the returned {@code Object[]} that should be used for the assignment. + * + * @return the index of the {@code Object[]} that should be used for the assignment. + */ int index() default -1; } diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/package-info.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/package-info.java index 1a873c0d7a..a8c490a24d 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/package-info.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/package-info.java @@ -11,9 +11,9 @@ * the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -22,6 +22,34 @@ * under the License. * #L% */ +/** + * A limitation of non-{@linkplain net.bytebuddy.asm.Advice.OnMethodEnter#inline() inlined advices} is that the {@code readOnly} property + * of annotations that bind values to advice method parameters cannot be used. + *

+ * Because we make heavy use of non-inlined advices for + * {@linkplain co.elastic.apm.agent.bci.ElasticApmInstrumentation#indyPlugin() indy plugins}, + * this package provides alternative means to bind values: + *

+ *
    + *
  • + * {@link co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToArgument}: + * Substitute of {@link net.bytebuddy.asm.Advice.Argument#readOnly()}. + *
  • + *
  • + * {@link co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToField}: + * Substitute of {@link net.bytebuddy.asm.Advice.FieldValue#readOnly()}. + *
  • + *
  • + * {@link co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToReturn}: + * Substitute of {@link net.bytebuddy.asm.Advice.Return#readOnly()}. + *
  • + *
  • + * {@link co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignTo}: + * Substitute of binding multiple values in a single method. + * Works by returning an {@code Object[]} from the advice method. + *
  • + *
+ */ @NonnullApi package co.elastic.apm.agent.bci.bytebuddy.postprocessor; diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/threadlocal/RemoveOnGetThreadLocal.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/threadlocal/RemoveOnGetThreadLocal.java index 46728d816c..b4fc8af159 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/threadlocal/RemoveOnGetThreadLocal.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/threadlocal/RemoveOnGetThreadLocal.java @@ -30,6 +30,12 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; +/** + * Allows registering a globally shared instance of a {@link DetachedThreadLocal} that allows for removal on get. + * + * @param + * @see co.elastic.apm.agent.util.GlobalVariables + */ public class RemoveOnGetThreadLocal extends DetachedThreadLocal { private static final ConcurrentMap> registry = new ConcurrentHashMap<>(); diff --git a/apm-agent-core/src/test/java/co/elastic/apm/agent/bci/InstrumentationTest.java b/apm-agent-core/src/test/java/co/elastic/apm/agent/bci/InstrumentationTest.java index 76edca4bdc..f7a88d2855 100644 --- a/apm-agent-core/src/test/java/co/elastic/apm/agent/bci/InstrumentationTest.java +++ b/apm-agent-core/src/test/java/co/elastic/apm/agent/bci/InstrumentationTest.java @@ -29,12 +29,12 @@ import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToArgument; import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToField; import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToReturn; +import co.elastic.apm.agent.bci.subpackage.AdviceInSubpackageInstrumentation; import co.elastic.apm.agent.configuration.CoreConfiguration; import co.elastic.apm.agent.configuration.SpyConfiguration; import co.elastic.apm.agent.impl.ElasticApmTracer; import co.elastic.apm.agent.impl.ElasticApmTracerBuilder; import co.elastic.apm.agent.matcher.WildcardMatcher; -import co.elastic.apm.agent.bci.subpackage.AdviceInSubpackageInstrumentation; import co.elastic.apm.agent.util.GlobalVariables; import net.bytebuddy.agent.ByteBuddyAgent; import net.bytebuddy.asm.Advice; @@ -661,7 +661,7 @@ public Collection getInstrumentationGroupNames() { } @Override - public boolean indyDispatch() { + public boolean indyPlugin() { return true; } } @@ -697,7 +697,7 @@ public Collection getInstrumentationGroupNames() { } @Override - public boolean indyDispatch() { + public boolean indyPlugin() { return true; } } @@ -733,7 +733,7 @@ public Collection getInstrumentationGroupNames() { } @Override - public boolean indyDispatch() { + public boolean indyPlugin() { return true; } } @@ -769,7 +769,7 @@ public Collection getInstrumentationGroupNames() { } @Override - public boolean indyDispatch() { + public boolean indyPlugin() { return true; } } @@ -798,7 +798,7 @@ public Collection getInstrumentationGroupNames() { } @Override - public boolean indyDispatch() { + public boolean indyPlugin() { return true; } } @@ -825,7 +825,7 @@ public Collection getInstrumentationGroupNames() { } @Override - public boolean indyDispatch() { + public boolean indyPlugin() { return true; } } diff --git a/apm-agent-core/src/test/java/co/elastic/apm/agent/bci/subpackage/AdviceInSubpackageInstrumentation.java b/apm-agent-core/src/test/java/co/elastic/apm/agent/bci/subpackage/AdviceInSubpackageInstrumentation.java index 668d97679d..1915668a0a 100644 --- a/apm-agent-core/src/test/java/co/elastic/apm/agent/bci/subpackage/AdviceInSubpackageInstrumentation.java +++ b/apm-agent-core/src/test/java/co/elastic/apm/agent/bci/subpackage/AdviceInSubpackageInstrumentation.java @@ -11,9 +11,9 @@ * the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -57,7 +57,7 @@ public Collection getInstrumentationGroupNames() { } @Override - public boolean indyDispatch() { + public boolean indyPlugin() { return true; } } diff --git a/apm-agent-plugins/apm-jdbc-plugin/src/main/java/co/elastic/apm/agent/jdbc/JdbcInstrumentation.java b/apm-agent-plugins/apm-jdbc-plugin/src/main/java/co/elastic/apm/agent/jdbc/JdbcInstrumentation.java index f97cac4b3d..1761409a1e 100644 --- a/apm-agent-plugins/apm-jdbc-plugin/src/main/java/co/elastic/apm/agent/jdbc/JdbcInstrumentation.java +++ b/apm-agent-plugins/apm-jdbc-plugin/src/main/java/co/elastic/apm/agent/jdbc/JdbcInstrumentation.java @@ -42,7 +42,7 @@ public final Collection getInstrumentationGroupNames() { } @Override - public boolean indyDispatch() { + public boolean indyPlugin() { return true; } } diff --git a/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/AbstractServletInstrumentation.java b/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/AbstractServletInstrumentation.java index 29df56ff53..72a40218a3 100644 --- a/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/AbstractServletInstrumentation.java +++ b/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/AbstractServletInstrumentation.java @@ -49,7 +49,7 @@ public ElementMatcher.Junction getClassLoaderMatcher() { } @Override - public boolean indyDispatch() { + public boolean indyPlugin() { return true; } } From 6771a07f6c03b0ee0b39ac46908dce5d3bb15f2b Mon Sep 17 00:00:00 2001 From: Felix Barnsteiner Date: Sun, 14 Jun 2020 17:35:01 +0200 Subject: [PATCH 17/35] Fix tests --- .../ApacheHttpAsyncClientInstrumentation.java | 11 ++++++---- ...asticsearchClientAsyncInstrumentation.java | 4 +++- ...asticsearchClientAsyncInstrumentation.java | 4 +++- .../OkHttp3ClientAsyncInstrumentation.java | 18 +++++++++-------- .../okhttp/OkHttp3ClientInstrumentation.java | 5 +++-- .../OkHttpClientAsyncInstrumentation.java | 20 ++++++++++--------- .../okhttp/OkHttpClientInstrumentation.java | 3 ++- .../impl/ApmSpanInstrumentation.java | 6 +++--- 8 files changed, 42 insertions(+), 29 deletions(-) diff --git a/apm-agent-plugins/apm-apache-httpclient-plugin/src/main/java/co/elastic/apm/agent/httpclient/ApacheHttpAsyncClientInstrumentation.java b/apm-agent-plugins/apm-apache-httpclient-plugin/src/main/java/co/elastic/apm/agent/httpclient/ApacheHttpAsyncClientInstrumentation.java index 921260c3a8..38340d791a 100644 --- a/apm-agent-plugins/apm-apache-httpclient-plugin/src/main/java/co/elastic/apm/agent/httpclient/ApacheHttpAsyncClientInstrumentation.java +++ b/apm-agent-plugins/apm-apache-httpclient-plugin/src/main/java/co/elastic/apm/agent/httpclient/ApacheHttpAsyncClientInstrumentation.java @@ -107,15 +107,18 @@ public static class ApacheHttpAsyncClientAdvice { @AssignToArgument(index = 0, value = 0), @AssignToArgument(index = 1, value = 3) }) + @Nullable @Advice.OnMethodEnter(suppress = Throwable.class) public static Object[] onBeforeExecute(@Advice.Argument(value = 0) HttpAsyncRequestProducer requestProducer, @Advice.Argument(2) HttpContext context, @Advice.Argument(value = 3) FutureCallback futureCallback) { if (tracer == null || tracer.getActive() == null) { - return new Object[]{requestProducer, futureCallback, false, null}; + return null; } final AbstractSpan parent = tracer.getActive(); Span span = parent.createExitSpan(); + HttpAsyncRequestProducer wrappedProducer = requestProducer; + FutureCallback wrappedFutureCallback = futureCallback; boolean wrapped = false; if (span != null) { span.withType(HttpClientHelper.EXTERNAL_TYPE) @@ -126,12 +129,12 @@ public static Object[] onBeforeExecute(@Advice.Argument(value = 0) HttpAsyncRequ asyncHelperManager.getForClassLoaderOfClass(HttpAsyncRequestProducer.class); TextHeaderSetter headerSetter = headerSetterHelperClassManager.getForClassLoaderOfClass(HttpRequest.class); if (asyncHelper != null && headerSetter != null) { - requestProducer = asyncHelper.wrapRequestProducer(requestProducer, span, headerSetter); - futureCallback = asyncHelper.wrapFutureCallback(futureCallback, context, span); + wrappedProducer = asyncHelper.wrapRequestProducer(requestProducer, span, headerSetter); + wrappedFutureCallback = asyncHelper.wrapFutureCallback(futureCallback, context, span); wrapped = true; } } - return new Object[]{requestProducer, futureCallback, wrapped, span}; + return new Object[]{wrappedProducer, wrappedFutureCallback, wrapped, span}; } @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) diff --git a/apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-5_6/src/main/java/co/elastic/apm/agent/es/restclient/v5_6/ElasticsearchClientAsyncInstrumentation.java b/apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-5_6/src/main/java/co/elastic/apm/agent/es/restclient/v5_6/ElasticsearchClientAsyncInstrumentation.java index 93d9a1c57b..5e53ecda30 100644 --- a/apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-5_6/src/main/java/co/elastic/apm/agent/es/restclient/v5_6/ElasticsearchClientAsyncInstrumentation.java +++ b/apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-5_6/src/main/java/co/elastic/apm/agent/es/restclient/v5_6/ElasticsearchClientAsyncInstrumentation.java @@ -24,6 +24,7 @@ */ package co.elastic.apm.agent.es.restclient.v5_6; +import co.elastic.apm.agent.bci.VisibleForAdvice; import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToArgument; import co.elastic.apm.agent.es.restclient.ElasticsearchRestClientInstrumentation; import co.elastic.apm.agent.es.restclient.ElasticsearchRestClientInstrumentationHelper; @@ -76,7 +77,8 @@ public ElementMatcher getMethodMatcher() { public static class ElasticsearchRestClientAsyncAdvice { - private static final RemoveOnGetThreadLocal spanTls = RemoveOnGetThreadLocal.get(ElasticsearchRestClientAsyncAdvice.class, "spanTls"); + @VisibleForAdvice + public static final RemoveOnGetThreadLocal spanTls = RemoveOnGetThreadLocal.get(ElasticsearchRestClientAsyncAdvice.class, "spanTls"); @AssignToArgument(5) @Advice.OnMethodEnter(suppress = Throwable.class) public static ResponseListener onBeforeExecute(@Advice.Argument(0) String method, diff --git a/apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-6_4/src/main/java/co/elastic/apm/agent/es/restclient/v6_4/ElasticsearchClientAsyncInstrumentation.java b/apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-6_4/src/main/java/co/elastic/apm/agent/es/restclient/v6_4/ElasticsearchClientAsyncInstrumentation.java index a4da26d880..8b65462eb8 100644 --- a/apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-6_4/src/main/java/co/elastic/apm/agent/es/restclient/v6_4/ElasticsearchClientAsyncInstrumentation.java +++ b/apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-6_4/src/main/java/co/elastic/apm/agent/es/restclient/v6_4/ElasticsearchClientAsyncInstrumentation.java @@ -24,6 +24,7 @@ */ package co.elastic.apm.agent.es.restclient.v6_4; +import co.elastic.apm.agent.bci.VisibleForAdvice; import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToArgument; import co.elastic.apm.agent.es.restclient.ElasticsearchRestClientInstrumentation; import co.elastic.apm.agent.es.restclient.ElasticsearchRestClientInstrumentationHelper; @@ -70,7 +71,8 @@ public ElementMatcher getMethodMatcher() { } public static class ElasticsearchRestClientAsyncAdvice { - private static final RemoveOnGetThreadLocal spanTls = RemoveOnGetThreadLocal.get(ElasticsearchRestClientAsyncAdvice.class, "spanTls"); + @VisibleForAdvice + public static final RemoveOnGetThreadLocal spanTls = RemoveOnGetThreadLocal.get(ElasticsearchRestClientAsyncAdvice.class, "spanTls"); @AssignToArgument(1) @Advice.OnMethodEnter(suppress = Throwable.class) diff --git a/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttp3ClientAsyncInstrumentation.java b/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttp3ClientAsyncInstrumentation.java index 048de64f1a..432cbec5be 100644 --- a/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttp3ClientAsyncInstrumentation.java +++ b/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttp3ClientAsyncInstrumentation.java @@ -86,22 +86,24 @@ public static class OkHttpClient3ExecuteAdvice { fields = @AssignToField(index = 0, value = "originalRequest"), arguments = @AssignToArgument(index = 1, value = 0) ) + @Nullable @Advice.OnMethodEnter(suppress = Throwable.class) - public static Object[] onBeforeEnqueue(@Advice.Origin Class clazz, - @Advice.FieldValue("originalRequest") @Nullable okhttp3.Request originalRequest, - @Advice.Argument(0) @Nullable Callback callback) { + public static Object[] onBeforeEnqueue(final @Advice.Origin Class clazz, + final @Advice.FieldValue("originalRequest") @Nullable okhttp3.Request originalRequest, + final @Advice.Argument(0) @Nullable Callback originalCallback) { if (tracer == null || tracer.getActive() == null || callbackWrapperCreator == null) { - return new Object[] {originalRequest, callback, null}; + return null; } final WrapperCreator wrapperCreator = callbackWrapperCreator.getForClassLoaderOfClass(clazz); - if (originalRequest == null || callback == null || wrapperCreator == null) { - return new Object[] {originalRequest, callback, null}; + if (originalRequest == null || originalCallback == null || wrapperCreator == null) { + return null; } final AbstractSpan parent = tracer.getActive(); okhttp3.Request request = originalRequest; + Callback callback = originalCallback; HttpUrl url = request.url(); Span span = HttpClientHelper.startHttpClientSpan(parent, request.method(), url.toString(), url.scheme(), OkHttpClientHelper.computeHostName(url.host()), url.port()); @@ -112,12 +114,12 @@ public static Object[] onBeforeEnqueue(@Advice.Origin Class claz if (headerSetter != null) { Request.Builder builder = originalRequest.newBuilder(); span.propagateTraceContext(builder, headerSetter); - originalRequest = builder.build(); + request = builder.build(); } } callback = wrapperCreator.wrap(callback, span); } - return new Object[] {originalRequest, callback, span}; + return new Object[] {request, callback, span}; } @Advice.OnMethodExit(suppress = Throwable.class) diff --git a/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttp3ClientInstrumentation.java b/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttp3ClientInstrumentation.java index deed505ef7..340e8f3c58 100644 --- a/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttp3ClientInstrumentation.java +++ b/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttp3ClientInstrumentation.java @@ -59,12 +59,13 @@ public Class getAdviceClass() { @VisibleForAdvice public static class OkHttpClient3ExecuteAdvice { - private final static RemoveOnGetThreadLocal spanTls = RemoveOnGetThreadLocal.get(OkHttpClient3ExecuteAdvice.class, "spanTls"); + @VisibleForAdvice + public final static RemoveOnGetThreadLocal spanTls = RemoveOnGetThreadLocal.get(OkHttpClient3ExecuteAdvice.class, "spanTls"); @Nullable @AssignToField(value = "originalRequest", typing = Assigner.Typing.DYNAMIC) @Advice.OnMethodEnter(suppress = Throwable.class) - public static Object onBeforeExecute(@Advice.FieldValue("originalRequest") @Nullable Object originalRequest) { + public static Object onBeforeExecute(final @Advice.FieldValue("originalRequest") @Nullable Object originalRequest) { if (tracer == null || tracer.getActive() == null || !(originalRequest instanceof Request)) { return originalRequest; diff --git a/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttpClientAsyncInstrumentation.java b/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttpClientAsyncInstrumentation.java index 3c7553644b..9c88e26e1d 100644 --- a/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttpClientAsyncInstrumentation.java +++ b/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttpClientAsyncInstrumentation.java @@ -84,22 +84,24 @@ public static class OkHttpClient3ExecuteAdvice { fields = @AssignToField(index = 0, value = "originalRequest"), arguments = @AssignToArgument(index = 1, value = 0) ) + @Nullable @Advice.OnMethodEnter(suppress = Throwable.class) - public static Object[] onBeforeEnqueue(@Advice.Origin Class clazz, - @Advice.FieldValue("originalRequest") @Nullable Request originalRequest, - @Advice.Argument(0) @Nullable Callback callback) { + public static Object[] onBeforeEnqueue(final @Advice.Origin Class clazz, + final @Advice.FieldValue("originalRequest") @Nullable Request originalRequest, + final @Advice.Argument(0) @Nullable Callback originalCallback) { if (tracer == null || tracer.getActive() == null || callbackWrapperCreator == null) { - return new Object[] {originalRequest, callback, null}; + return null; } final WrapperCreator wrapperCreator = callbackWrapperCreator.getForClassLoaderOfClass(clazz); - if (originalRequest == null || callback == null || wrapperCreator == null) { - return new Object[] {originalRequest, callback, null}; + if (originalRequest == null || originalCallback == null || wrapperCreator == null) { + return null; } final AbstractSpan parent = tracer.getActive(); Request request = originalRequest; + Callback callback = originalCallback; URL url = request.url(); Span span = HttpClientHelper.startHttpClientSpan(parent, request.method(), url.toString(), url.getProtocol(), OkHttpClientHelper.computeHostName(url.getHost()), url.getPort()); @@ -110,12 +112,12 @@ public static Object[] onBeforeEnqueue(@Advice.Origin Class claz if (headerSetter != null) { Request.Builder builder = originalRequest.newBuilder(); span.propagateTraceContext(builder, headerSetter); - originalRequest = builder.build(); + request = builder.build(); } } - callback = wrapperCreator.wrap(callback, span); + callback = wrapperCreator.wrap(originalCallback, span); } - return new Object[] {originalRequest, callback, span}; + return new Object[] {request, callback, span}; } @Advice.OnMethodExit(suppress = Throwable.class) diff --git a/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttpClientInstrumentation.java b/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttpClientInstrumentation.java index 9a5db14075..d958c25ab9 100644 --- a/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttpClientInstrumentation.java +++ b/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttpClientInstrumentation.java @@ -58,7 +58,8 @@ public Class getAdviceClass() { @VisibleForAdvice public static class OkHttpClientExecuteAdvice { - private final static RemoveOnGetThreadLocal spanTls = RemoveOnGetThreadLocal.get(OkHttpClientExecuteAdvice.class, "spanTls"); + @VisibleForAdvice + public final static RemoveOnGetThreadLocal spanTls = RemoveOnGetThreadLocal.get(OkHttpClientExecuteAdvice.class, "spanTls"); @Nullable @AssignToField(value = "originalRequest", typing = Assigner.Typing.DYNAMIC) diff --git a/apm-agent-plugins/apm-opentracing-plugin/src/main/java/co/elastic/apm/agent/opentracing/impl/ApmSpanInstrumentation.java b/apm-agent-plugins/apm-opentracing-plugin/src/main/java/co/elastic/apm/agent/opentracing/impl/ApmSpanInstrumentation.java index 26c5b61e5c..0be3a33f70 100644 --- a/apm-agent-plugins/apm-opentracing-plugin/src/main/java/co/elastic/apm/agent/opentracing/impl/ApmSpanInstrumentation.java +++ b/apm-agent-plugins/apm-opentracing-plugin/src/main/java/co/elastic/apm/agent/opentracing/impl/ApmSpanInstrumentation.java @@ -112,7 +112,7 @@ public SetOperationName() { } @VisibleForAdvice - @Advice.OnMethodEnter(suppress = Throwable.class) + @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) public static void setOperationName(@Advice.FieldValue(value = "dispatcher", typing = Assigner.Typing.DYNAMIC) @Nullable AbstractSpan span, @Advice.Argument(0) @Nullable String operationName) { if (span != null) { @@ -129,7 +129,7 @@ public LogInstrumentation() { } @VisibleForAdvice - @Advice.OnMethodEnter(suppress = Throwable.class) + @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) public static void log(@Advice.FieldValue(value = "dispatcher", typing = Assigner.Typing.DYNAMIC) @Nullable AbstractSpan span, @Advice.Argument(0) long epochTimestampMicros, @Advice.Argument(1) Map fields) { @@ -158,7 +158,7 @@ public TagInstrumentation() { } @VisibleForAdvice - @Advice.OnMethodEnter(suppress = Throwable.class) + @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) public static void handleTag(@Advice.FieldValue(value = "dispatcher", typing = Assigner.Typing.DYNAMIC) @Nullable AbstractSpan span, @Advice.Argument(0) String key, @Advice.Argument(1) @Nullable Object value) { From 87f324f995072547d6069c89e2b9ae6968cf1909 Mon Sep 17 00:00:00 2001 From: Felix Barnsteiner Date: Mon, 15 Jun 2020 14:00:03 +0200 Subject: [PATCH 18/35] Update Byte Buddy --- .../elastic/apm/agent/bci/IndyBootstrap.java | 9 ++++----- .../bootstrap/IndyBootstrapDispatcher.clazz | Bin 3401 -> 3388 bytes .../bootstrap/IndyBootstrapDispatcher.java | 13 +++++++------ .../apm/agent/bci/InstrumentationTest.java | 17 ++++++----------- pom.xml | 2 +- 5 files changed, 18 insertions(+), 23 deletions(-) diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/IndyBootstrap.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/IndyBootstrap.java index d2863a0dd5..a1449f3299 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/IndyBootstrap.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/IndyBootstrap.java @@ -172,9 +172,9 @@ public static Method getIndyBootstrapMethod() { indyBootstrapClass .getField("bootstrap") .set(null, IndyBootstrap.class.getMethod("bootstrap", MethodHandles.Lookup.class, String.class, MethodType.class, - String.class, Class.class, MethodHandle.class, String.class, int.class)); + String.class, int.class, Class.class, String.class, Object[].class)); return indyBootstrapMethod = indyBootstrapClass.getMethod("bootstrap", MethodHandles.Lookup.class, String.class, - MethodType.class, String.class, Class.class, MethodHandle.class, String.class, int.class); + MethodType.class, String.class, int.class, Class.class, String.class, Object[].class); } catch (Exception e) { throw new RuntimeException(e); } @@ -218,15 +218,14 @@ private static Class initIndyBootstrap() throws Exception { * Exceptions and {@code null} return values are handled by {@code java.lang.IndyBootstrapDispatcher#bootstrap}. *

*/ - @Nullable public static ConstantCallSite bootstrap(MethodHandles.Lookup lookup, String adviceMethodName, MethodType adviceMethodType, String adviceClassName, + int enter, Class instrumentedType, - MethodHandle instrumentedMethod, String instrumentedMethodName, - int enter) throws Exception { + Object... args) throws Exception { Class adviceClass = Class.forName(adviceClassName); String packageName = adviceClass.getPackage().getName(); List pluginClasses = classesByPackage.get(packageName); diff --git a/apm-agent-core/src/main/resources/bootstrap/IndyBootstrapDispatcher.clazz b/apm-agent-core/src/main/resources/bootstrap/IndyBootstrapDispatcher.clazz index 5404e5696060da6ebb124c7e12be2874c1eb917e..548a2134101bc855be31e1da8a3b7aaf2f72487d 100644 GIT binary patch delta 749 zcmZ8fTTc^V5ItYJyVG615P|_yC;}QQiE@dc0tGD>(F%xKL=hAUtmV4YUQk4>cSR}2 zd@sKFVB)(WCPs-S#uxn+zWNi4MxAYZ;$?Q`>^WyH;k7{x^kmoVmrahDQvu?fPYgl- zT8D_JHxy7gjTBL(`c9+*o?jKZ?WFCbfQSeb%{ZxaGPB@9Q-jM1TeW-3%e7cC&Su+2 z`etKGvHJO#6R#hbn6)RqmEP2{ger+zTqRtSSVo;h1$IbW#|q(w#7*2HWF>Cnj>KKulemuu z5)bi6<@6fku|xw_)dxN3dxEDDYsje|dbPGLuv=AY+f~>s+-Nj9HLoq!IAu=zF4_X5PJ~9F8=wo=)qnb$3FC&7e_(p&KF0CUAZFTH_8|+{*59+QO{4dJjP-Vk8UEO*5yt;%*{@y ex6%o^wgk@*>rAHf=81i%;im<`*EY)E@Baq{wU5{U delta 795 zcmaKq$xjqP6vlrI-CaFR9Y(e|M8PFN+`ug?j*0>b$f7ccfC`KgjRm<*ds&&ikRym-PGGfFSCU%LV^( zQ84Jx=qFS=YO2oaPaI^aE7)lCEhrGg`Zx=gGWG-zN5?W%*Ymnjm|=PnZz1RH*8%cHtD%dezS&B;wx jt$wUYy_8<5Z!5^^(scnXY0e0bQ>ITBL+@rUTL}FIg;J!S diff --git a/apm-agent-core/src/main/resources/bootstrap/IndyBootstrapDispatcher.java b/apm-agent-core/src/main/resources/bootstrap/IndyBootstrapDispatcher.java index aed1a54a91..dc0c624039 100644 --- a/apm-agent-core/src/main/resources/bootstrap/IndyBootstrapDispatcher.java +++ b/apm-agent-core/src/main/resources/bootstrap/IndyBootstrapDispatcher.java @@ -8,14 +8,14 @@ import java.lang.reflect.Array; import java.lang.reflect.Method; -public class IndyBootstrap { +public class IndyBootstrapDispatcher { public static Method bootstrap; private static final MethodHandle VOID_NOOP; static { try { - VOID_NOOP = MethodHandles.lookup().findStatic(IndyBootstrap.class, "voidNoop", MethodType.methodType(void.class)); + VOID_NOOP = MethodHandles.lookup().findStatic(IndyBootstrapDispatcher.class, "voidNoop", MethodType.methodType(void.class)); } catch (Exception e) { throw new RuntimeException(e); } @@ -25,10 +25,10 @@ public static CallSite bootstrap(MethodHandles.Lookup lookup, String adviceMethodName, MethodType adviceMethodType, String adviceClassName, + int enter, Class instrumentedType, - MethodHandle instrumentedMethod, String instrumentedMethodName, - int enter) { + Object... args) { CallSite callSite = null; if (bootstrap != null) { try { @@ -36,9 +36,10 @@ public static CallSite bootstrap(MethodHandles.Lookup lookup, adviceMethodName, adviceMethodType, adviceClassName, + enter, instrumentedType, - instrumentedMethod, - instrumentedMethodName, enter); + instrumentedMethodName, + args); } catch (Exception e) { e.printStackTrace(); } diff --git a/apm-agent-core/src/test/java/co/elastic/apm/agent/bci/InstrumentationTest.java b/apm-agent-core/src/test/java/co/elastic/apm/agent/bci/InstrumentationTest.java index f7a88d2855..894d700179 100644 --- a/apm-agent-core/src/test/java/co/elastic/apm/agent/bci/InstrumentationTest.java +++ b/apm-agent-core/src/test/java/co/elastic/apm/agent/bci/InstrumentationTest.java @@ -49,7 +49,6 @@ import org.apache.commons.pool2.impl.CallStackUtils; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.slf4j.event.SubstituteLoggingEvent; import org.stagemonitor.configuration.ConfigurationRegistry; @@ -283,7 +282,6 @@ String exceptionPlease() { } @Test - @Disabled void testPatchClassFileVersionJava6ToJava7() { // loading classes compiled with bytecode level 50 (Java 6) assertThat(StringUtils.startsWithIgnoreCase("APM", "apm")).isTrue(); @@ -303,7 +301,6 @@ void testPatchClassFileVersionJava6ToJava7() { } @Test - @Disabled void testPatchClassFileVersionJava5ToJava7() { // loading classes compiled with bytecode level 49 (Java 6) new org.slf4j.event.SubstituteLoggingEvent(); @@ -323,7 +320,6 @@ void testPatchClassFileVersionJava5ToJava7() { } @Test - @Disabled void testPatchClassFileVersionJava5ToJava7CommonsMath() { org.apache.commons.math3.stat.StatUtils.max(new double[]{3.14}); @@ -342,7 +338,6 @@ void testPatchClassFileVersionJava5ToJava7CommonsMath() { } @Test - @Disabled void testPrivateConstructorJava7() { org.apache.commons.pool2.impl.CallStackUtils.newCallStack("", false, false); @@ -668,8 +663,8 @@ public boolean indyPlugin() { public static class LoggerFactoryInstrumentation extends ElasticApmInstrumentation { - public static AtomicInteger enterCount = GlobalVariables.get(CommonsLangInstrumentation.class, "enterCount", new AtomicInteger()); - public static AtomicInteger exitCount = GlobalVariables.get(CommonsLangInstrumentation.class, "exitCount", new AtomicInteger()); + public static AtomicInteger enterCount = GlobalVariables.get(LoggerFactoryInstrumentation.class, "enterCount", new AtomicInteger()); + public static AtomicInteger exitCount = GlobalVariables.get(LoggerFactoryInstrumentation.class, "exitCount", new AtomicInteger()); @Advice.OnMethodEnter(inline = false) public static void onEnter() { @@ -704,8 +699,8 @@ public boolean indyPlugin() { public static class StatUtilsInstrumentation extends ElasticApmInstrumentation { - public static AtomicInteger enterCount = GlobalVariables.get(CommonsLangInstrumentation.class, "enterCount", new AtomicInteger()); - public static AtomicInteger exitCount = GlobalVariables.get(CommonsLangInstrumentation.class, "exitCount", new AtomicInteger()); + public static AtomicInteger enterCount = GlobalVariables.get(StatUtilsInstrumentation.class, "enterCount", new AtomicInteger()); + public static AtomicInteger exitCount = GlobalVariables.get(StatUtilsInstrumentation.class, "exitCount", new AtomicInteger()); @Advice.OnMethodEnter(inline = false) public static void onEnter() { @@ -740,8 +735,8 @@ public boolean indyPlugin() { public static class CallStackUtilsInstrumentation extends ElasticApmInstrumentation { - public static AtomicInteger enterCount = GlobalVariables.get(CommonsLangInstrumentation.class, "enterCount", new AtomicInteger()); - public static AtomicInteger exitCount = GlobalVariables.get(CommonsLangInstrumentation.class, "exitCount", new AtomicInteger()); + public static AtomicInteger enterCount = GlobalVariables.get(CallStackUtilsInstrumentation.class, "enterCount", new AtomicInteger()); + public static AtomicInteger exitCount = GlobalVariables.get(CallStackUtilsInstrumentation.class, "exitCount", new AtomicInteger()); @Advice.OnMethodEnter(inline = false) public static void onEnter() { diff --git a/pom.xml b/pom.xml index 1eb4372f98..8105044138 100644 --- a/pom.xml +++ b/pom.xml @@ -96,7 +96,7 @@ 5.0.15.RELEASE 9.4.11.v20180605 0.1.19 - 1.10.11 + 1.10.12-SNAPSHOT 1.17 From 8553bb612b6de200a8cfbeb1a0834ea630394262 Mon Sep 17 00:00:00 2001 From: Felix Barnsteiner Date: Tue, 16 Jun 2020 11:23:02 +0200 Subject: [PATCH 19/35] Use MutableInt instead of AtomicInteger for CallDepth --- .../co/elastic/apm/agent/util/CallDepth.java | 44 +++++++++++++++---- .../elastic/apm/agent/util/CallDepthTest.java | 36 +++++++++++++++ 2 files changed, 71 insertions(+), 9 deletions(-) create mode 100644 apm-agent-core/src/test/java/co/elastic/apm/agent/util/CallDepthTest.java diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/util/CallDepth.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/util/CallDepth.java index fc311d89a3..ac5c6db385 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/util/CallDepth.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/util/CallDepth.java @@ -28,18 +28,24 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.atomic.AtomicInteger; /** * A utility that makes it easy to detect nested method calls. */ public class CallDepth { private static final ConcurrentMap registry = new ConcurrentHashMap<>(); - private final ThreadLocal callDepthPerThread = new ThreadLocal(); + private final ThreadLocal callDepthPerThread = new ThreadLocal(); private CallDepth() { } + /** + * Returns or creates a globally shared call depth instance, based on the advice's class name. + * + * @param adviceClass the class of the advice the call depth is used in. + * @return a globally shared call depth instance, based on the advice's class name. + * @see GlobalVariables + */ public static CallDepth get(Class adviceClass) { // we want to return the same CallDepth instance even if the advice class has been loaded from different class loaders String key = adviceClass.getName(); @@ -51,6 +57,10 @@ public static CallDepth get(Class adviceClass) { return callDepth; } + static void clearRegistry() { + registry.clear(); + } + /** * Gets and increments the call depth counter. * Returns {@code 0} if this is the outer-most (non-nested) invocation. @@ -58,12 +68,7 @@ public static CallDepth get(Class adviceClass) { * @return the call depth before it has been incremented */ public int increment() { - AtomicInteger callDepthForCurrentThread = callDepthPerThread.get(); - if (callDepthForCurrentThread == null) { - callDepthForCurrentThread = new AtomicInteger(); - callDepthPerThread.set(callDepthForCurrentThread); - } - return callDepthForCurrentThread.getAndIncrement(); + return get().getAndIncrement(); } /** @@ -87,7 +92,7 @@ public boolean isNestedCallAndIncrement() { * @return the call depth after it has been incremented */ public int decrement() { - int depth = callDepthPerThread.get().decrementAndGet(); + int depth = get().decrementAndGet(); assert depth >= 0; return depth; } @@ -105,4 +110,25 @@ public int decrement() { public boolean isNestedCallAndDecrement() { return decrement() != 0; } + + private MutableInt get() { + MutableInt callDepthForCurrentThread = callDepthPerThread.get(); + if (callDepthForCurrentThread == null) { + callDepthForCurrentThread = new MutableInt(); + callDepthPerThread.set(callDepthForCurrentThread); + } + return callDepthForCurrentThread; + } + + private static class MutableInt { + private int i; + + public int getAndIncrement() { + return i++; + } + + public int decrementAndGet() { + return --i; + } + } } diff --git a/apm-agent-core/src/test/java/co/elastic/apm/agent/util/CallDepthTest.java b/apm-agent-core/src/test/java/co/elastic/apm/agent/util/CallDepthTest.java new file mode 100644 index 0000000000..2683c8f0a7 --- /dev/null +++ b/apm-agent-core/src/test/java/co/elastic/apm/agent/util/CallDepthTest.java @@ -0,0 +1,36 @@ +package co.elastic.apm.agent.util; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class CallDepthTest { + + private CallDepth callDepth; + + @BeforeEach + void setUp() { + callDepth = CallDepth.get(CallDepthTest.class); + } + + @AfterEach + void tearDown() { + CallDepth.clearRegistry(); + } + + @Test + void testDetectNestedCalls() { + assertThat(callDepth.isNestedCallAndIncrement()).isFalse(); + assertThat(callDepth.isNestedCallAndIncrement()).isTrue(); + assertThat(callDepth.isNestedCallAndDecrement()).isTrue(); + assertThat(callDepth.isNestedCallAndDecrement()).isFalse(); + } + + @Test + void testNegativeCount() { + assertThatThrownBy(() -> callDepth.decrement()).isInstanceOf(AssertionError.class); + } +} From 6abbf01aa4b5e1c68dec3679822679b6b54676c3 Mon Sep 17 00:00:00 2001 From: Felix Barnsteiner Date: Thu, 18 Jun 2020 12:34:47 +0200 Subject: [PATCH 20/35] Make concurrent, process, and HttpUrlConnection indy plugins - Fix matcher for ForkJoinPool - Add support for parallel streams - Simplify testing of bootstrap instrumentations - Remove java.* from the default exclusions - Disallow agent types in advice signature - Enable previously disabled async Dubbo tests --- .../apm/agent/bci/ElasticApmAgent.java | 59 ++- .../agent/bci/ElasticApmInstrumentation.java | 24 +- .../elastic/apm/agent/bci/IndyBootstrap.java | 6 +- .../TraceMethodInstrumentation.java | 5 + .../bootstrap/IndyBootstrapDispatcher.clazz | Bin 3388 -> 3403 bytes .../bootstrap/IndyBootstrapDispatcher.java | 2 +- .../elastic/apm/agent/util/CallDepthTest.java | 24 ++ .../src/test/resources/elasticapm.properties | 2 - .../dubbo/ApacheDubboInstrumentationTest.java | 7 - .../agent/dubbo/ExecutorServiceWrapper.java | 116 ------ .../dubbo/api/impl/DubboTestApiImpl.java | 3 +- .../concurrent/ExecutorInstrumentation.java | 110 +++--- .../ForkJoinTaskInstrumentation.java | 76 ++++ .../apm/agent/concurrent/JavaConcurrent.java | 21 +- ...leCallableForkJoinTaskInstrumentation.java | 20 +- .../apm/agent/concurrent/package-info.java | 8 +- ...ic.apm.agent.bci.ElasticApmInstrumentation | 1 + .../AsyncTraceMethodInstrumentationTest.java | 2 +- .../concurrent/ExcludedExecutorClassTest.java | 6 +- .../ExecutorServiceDoubleWrappingTest.java | 3 +- .../ExecutorServiceInstrumentationTest.java | 45 +-- .../concurrent/ExecutorServiceWrapper.java | 120 ------ .../agent/concurrent/ForkJoinPoolTest.java | 70 +++- .../InstrumentableForkJoinPool.java | 114 ------ .../RunnableWrapperExecutorService.java | 85 ---- .../agent/concurrent/ScopeManagementTest.java | 8 +- .../agent/jdbc/StatementInstrumentation.java | 38 +- .../process/BaseProcessInstrumentation.java | 4 + .../CommonsExecAsyncInstrumentation.java | 17 +- .../process/ProcessExitInstrumentation.java | 12 +- .../apm/agent/process/ProcessHelper.java | 10 +- .../process/ProcessStartInstrumentation.java | 2 +- .../HttpUrlConnectionInstrumentation.java | 23 +- .../SSLContextInstrumentation.java | 7 +- .../HttpUrlConnectionInstrumentationTest.java | 365 +----------------- 35 files changed, 409 insertions(+), 1006 deletions(-) delete mode 100644 apm-agent-plugins/apm-dubbo-plugin/src/test/java/co/elastic/apm/agent/dubbo/ExecutorServiceWrapper.java create mode 100644 apm-agent-plugins/apm-java-concurrent-plugin/src/main/java/co/elastic/apm/agent/concurrent/ForkJoinTaskInstrumentation.java delete mode 100644 apm-agent-plugins/apm-java-concurrent-plugin/src/test/java/co/elastic/apm/agent/concurrent/ExecutorServiceWrapper.java delete mode 100644 apm-agent-plugins/apm-java-concurrent-plugin/src/test/java/co/elastic/apm/agent/concurrent/InstrumentableForkJoinPool.java delete mode 100644 apm-agent-plugins/apm-java-concurrent-plugin/src/test/java/co/elastic/apm/agent/concurrent/RunnableWrapperExecutorService.java diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/ElasticApmAgent.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/ElasticApmAgent.java index 6dc25694bc..ca22255c9b 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/ElasticApmAgent.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/ElasticApmAgent.java @@ -52,6 +52,7 @@ import net.bytebuddy.description.NamedElement; import net.bytebuddy.description.annotation.AnnotationDescription; import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.description.method.ParameterDescription; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.dynamic.ClassFileLocator; import net.bytebuddy.dynamic.DynamicType; @@ -91,14 +92,12 @@ import static net.bytebuddy.asm.Advice.ExceptionHandler.Default.PRINTING; import static net.bytebuddy.matcher.ElementMatchers.any; import static net.bytebuddy.matcher.ElementMatchers.is; -import static net.bytebuddy.matcher.ElementMatchers.isAnnotatedWith; import static net.bytebuddy.matcher.ElementMatchers.isAbstract; +import static net.bytebuddy.matcher.ElementMatchers.isAnnotatedWith; import static net.bytebuddy.matcher.ElementMatchers.isInterface; import static net.bytebuddy.matcher.ElementMatchers.isStatic; import static net.bytebuddy.matcher.ElementMatchers.nameContains; -import static net.bytebuddy.matcher.ElementMatchers.nameEndsWith; import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith; -import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.none; import static net.bytebuddy.matcher.ElementMatchers.not; @@ -350,19 +349,15 @@ public boolean matches(MethodDescription target) { /** * Validates invariants explained in {@link ElasticApmInstrumentation#indyPlugin()} + * * @param adviceClassName the name of the advice class */ private static void validateAdvice(String adviceClassName) { - validateAdviceIsInlined(adviceClassName); - if (adviceClassName.startsWith("co.elastic.apm.agent.") && adviceClassName.split("\\.").length > 6) { - throw new IllegalStateException("Indy-dispatched advice class must be at the root of the instrumentation plugin."); - } - } - - private static void validateAdviceIsInlined(String adviceClassName) { TypePool pool = new TypePool.Default(TypePool.CacheProvider.NoOp.INSTANCE, ClassFileLocator.ForClassLoader.ofSystemLoader(), TypePool.Default.ReaderMode.FAST); TypeDescription typeDescription = pool.describe(adviceClassName).resolve(); for (MethodDescription.InDefinedShape enterAdvice : typeDescription.getDeclaredMethods().filter(isStatic().and(isAnnotatedWith(Advice.OnMethodEnter.class)))) { + validateAdviceReturnAndParameterTypes(enterAdvice); + for (AnnotationDescription enter : enterAdvice.getDeclaredAnnotations().filter(ElementMatchers.annotationType(Advice.OnMethodEnter.class))) { if (enter.prepare(Advice.OnMethodEnter.class).load().inline()) { throw new IllegalStateException(String.format("Indy-dispatched advice %s#%s has to be declared with inline=false", adviceClassName, enterAdvice.getName())); @@ -370,12 +365,30 @@ private static void validateAdviceIsInlined(String adviceClassName) { } } for (MethodDescription.InDefinedShape exitAdvice : typeDescription.getDeclaredMethods().filter(isStatic().and(isAnnotatedWith(Advice.OnMethodExit.class)))) { + validateAdviceReturnAndParameterTypes(exitAdvice); + if (exitAdvice.getReturnType().getTypeName().startsWith("co.elastic.apm")) { + throw new IllegalStateException("Advice return type must be visible from the bootstrap class loader and must not be an agent type."); + } for (AnnotationDescription exit : exitAdvice.getDeclaredAnnotations().filter(ElementMatchers.annotationType(Advice.OnMethodExit.class))) { if (exit.prepare(Advice.OnMethodExit.class).load().inline()) { throw new IllegalStateException(String.format("Indy-dispatched advice %s#%s has to be declared with inline=false", adviceClassName, exitAdvice.getName())); } } } + if (adviceClassName.startsWith("co.elastic.apm.agent.") && adviceClassName.split("\\.").length > 6) { + throw new IllegalStateException("Indy-dispatched advice class must be at the root of the instrumentation plugin."); + } + } + + private static void validateAdviceReturnAndParameterTypes(MethodDescription.InDefinedShape advice) { + if (advice.getReturnType().getTypeName().startsWith("co.elastic.apm")) { + throw new IllegalStateException("Advice return type must not be an agent type: " + advice.toGenericString()); + } + for (ParameterDescription.InDefinedShape parameter : advice.getParameters()) { + if (parameter.getName().startsWith("co.elastic.apm")) { + throw new IllegalStateException("Advice parameters must not contain an agent type: " + advice.toGenericString()); + } + } } private static MatcherTimer getOrCreateTimer(Class adviceClass) { @@ -493,32 +506,6 @@ public Iterable>> onError(int index, List> batc // avoids instrumenting classes from helper class loaders .or(any(), classLoaderWithName(ByteArrayClassLoader.ChildFirst.class.getName())) .or(any(), classLoaderWithName("org.codehaus.groovy.runtime.callsite.CallSiteClassLoader")) - // ideally, those bootstrap classpath inclusions should be set at plugin level, see issue #952 - .or(nameStartsWith("java.") - .and( - not( - nameEndsWith("URLConnection") - .or(nameStartsWith("java.util.concurrent.")) - .or(named("java.lang.ProcessBuilder")) - .or(named("java.lang.ProcessImpl")) - .or(named("java.lang.Process")) - .or(named("java.lang.UNIXProcess")) - ) - ) - ) - .or(nameStartsWith("com.sun.") - .and( - not( - nameStartsWith("com.sun.faces.") - .or(nameEndsWith("URLConnection")) - ) - ) - ) - .or(nameStartsWith("sun") - .and( - not(nameEndsWith("URLConnection")) - ) - ) .or(nameStartsWith("co.elastic.apm.agent.shaded")) .or(nameStartsWith("org.aspectj.")) .or(nameStartsWith("org.groovy.")) diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/ElasticApmInstrumentation.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/ElasticApmInstrumentation.java index 6129e4e475..e09684e82e 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/ElasticApmInstrumentation.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/ElasticApmInstrumentation.java @@ -206,8 +206,25 @@ public void onTypeMatch(TypeDescription typeDescription, ClassLoader classLoader *

*

* Things to watch out for when using indy plugins: + *

*
    *
  • + * Set {@link Advice.OnMethodEnter#inline()} and {@link Advice.OnMethodExit#inline()} to {@code false} on all advices. + * As the {@code readOnly} flag in Byte Buddy annotations such as {@link Advice.Return#readOnly()} cannot be used with non + * {@linkplain Advice.OnMethodEnter#inline() inlined advices}, + * use {@link co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignTo} and friends. + *
  • + *
  • + * Both the return type and the arguments of advice methods must no contain types from the agent. + * If you'd like to return a {@link Span} from an advice, for example, return an {@link Object} instead. + * When using an {@link net.bytebuddy.asm.Advice.Enter} argument on the + * {@linkplain net.bytebuddy.asm.Advice.OnMethodExit exit advice}, + * that argument als has to be of type {@link Object} and you have to cast in within the method body. + * The reason is that the return value will become a local variable in the instrumented method. + * Due to OSGi, those methods may not have access to agent types. + * Another case is when the instrumented class is inside the bootstrap classloader. + *
  • + *
  • * When an advice instruments classes in multiple class loaders, the plugin classes will be loaded form multiple class loaders. * In order to still share state across those plugin class loaders, use {@link co.elastic.apm.agent.util.GlobalVariables} or {@link GlobalState}. * That's necessary as a static variables are scoped to the class loader they are defined in. @@ -222,14 +239,7 @@ public void onTypeMatch(TypeDescription typeDescription, ClassLoader classLoader * As the package of the {@link #getAdviceClass()} is used as the root, * all advices have to be at the top level of the plugin. *
  • - *
  • - * Set {@link Advice.OnMethodEnter#inline()} and {@link Advice.OnMethodExit#inline()} to {@code false} on all advices. - * As the {@code readOnly} flag in Byte Buddy annotations such as {@link Advice.Return#readOnly()} cannot be used with non - * {@linkplain Advice.OnMethodEnter#inline() inlined advices}, - * use {@link co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignTo} and friends. - *
  • *
- *

* * @return whether to load the classes of this plugin in dedicated plugin class loaders (one for each unique class loader) * and dispatch to the {@linkplain #getAdviceClass() advice} via an {@code INVOKEDYNAMIC} instruction. diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/IndyBootstrap.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/IndyBootstrap.java index a1449f3299..47828a82c9 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/IndyBootstrap.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/IndyBootstrap.java @@ -42,6 +42,7 @@ import java.util.concurrent.ConcurrentMap; import static net.bytebuddy.matcher.ElementMatchers.isAnnotatedWith; +import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith; import static net.bytebuddy.matcher.ElementMatchers.named; /** @@ -236,7 +237,10 @@ public static ConstantCallSite bootstrap(MethodHandles.Lookup lookup, ClassLoader pluginClassLoader = HelperClassManager.ForIndyPlugin.getOrCreatePluginClassLoader( lookup.lookupClass().getClassLoader(), pluginClasses, - isAnnotatedWith(named(GlobalState.class.getName()))); + isAnnotatedWith(named(GlobalState.class.getName())) + // no plugin CL necessary as all types are available form bootstrap CL + // also, this plugin is used as a dependency in other plugins + .or(nameStartsWith("co.elastic.apm.agent.concurrent"))); Class adviceInPluginCL = pluginClassLoader.loadClass(adviceClassName); MethodHandle methodHandle = MethodHandles.lookup().findStatic(adviceInPluginCL, adviceMethodName, adviceMethodType); return new ConstantCallSite(methodHandle); diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/methodmatching/TraceMethodInstrumentation.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/methodmatching/TraceMethodInstrumentation.java index e79f7362ed..0e97cabcf0 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/methodmatching/TraceMethodInstrumentation.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/methodmatching/TraceMethodInstrumentation.java @@ -51,6 +51,7 @@ import static net.bytebuddy.matcher.ElementMatchers.isSynthetic; import static net.bytebuddy.matcher.ElementMatchers.isTypeInitializer; import static net.bytebuddy.matcher.ElementMatchers.nameContains; +import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith; import static net.bytebuddy.matcher.ElementMatchers.not; import static net.bytebuddy.matcher.ElementMatchers.takesArgument; import static net.bytebuddy.matcher.ElementMatchers.takesArguments; @@ -110,6 +111,10 @@ public ElementMatcher getTypeMatcher() { .and(not(nameContains("CGLIB"))) .and(not(nameContains("EnhancerBy"))) .and(not(nameContains("$Proxy"))) + .and(not(nameStartsWith("java."))) + .and(not(nameStartsWith("com.sun."))) + .and(not(nameStartsWith("sun."))) + .and(not(nameStartsWith("jdk."))) .and(declaresMethod(matches(methodMatcher.getMethodMatcher()))); } diff --git a/apm-agent-core/src/main/resources/bootstrap/IndyBootstrapDispatcher.clazz b/apm-agent-core/src/main/resources/bootstrap/IndyBootstrapDispatcher.clazz index 548a2134101bc855be31e1da8a3b7aaf2f72487d..989ce9d357363abc3564cafb0aad67e9662c15f7 100644 GIT binary patch delta 78 zcmV-U0I~nP8p|3A%Ko~(0000p0Fbc?p#=kp0E&~Y1ttiK0E`R(jR205+yx;4kF)*- k8UqmwaCKsAX=6-pZ)p#=jl0Ev^X1ttiJ0E-L&i~x<3+yx;4j shutdownNow() { - return delegate.shutdownNow(); - } - - @Override - public boolean isShutdown() { - return delegate.isShutdown(); - } - - @Override - public boolean isTerminated() { - return delegate.isTerminated(); - } - - @Override - public boolean awaitTermination(long timeout, @Nonnull TimeUnit unit) throws InterruptedException { - return delegate.awaitTermination(timeout, unit); - } - - // Runnable - - @Override - public void execute(@Nonnull final Runnable command) { - delegate.execute(command); - } - - @Override - public Future submit(@Nonnull final Runnable task) { - return delegate.submit(task); - } - - @Override - public Future submit(@Nonnull final Runnable task, T result) { - return delegate.submit(task, result); - } - - // Callable - - @Override - public Future submit(@Nonnull final Callable task) { - return delegate.submit(task); - } - - // Collection - - @Override - public List> invokeAll(@Nonnull Collection> tasks) throws InterruptedException { - return delegate.invokeAll(tasks); - } - - @Override - public List> invokeAll(@Nonnull Collection> tasks, long timeout, @Nonnull TimeUnit unit) throws InterruptedException { - return delegate.invokeAll(tasks, timeout, unit); - } - - @Override - public T invokeAny(@Nonnull Collection> tasks) throws InterruptedException, ExecutionException { - return delegate.invokeAny(tasks); - } - - @Override - public T invokeAny(@Nonnull Collection> tasks, long timeout, @Nonnull TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { - return delegate.invokeAny(tasks, timeout, unit); - } -} diff --git a/apm-agent-plugins/apm-dubbo-plugin/src/test/java/co/elastic/apm/agent/dubbo/api/impl/DubboTestApiImpl.java b/apm-agent-plugins/apm-dubbo-plugin/src/test/java/co/elastic/apm/agent/dubbo/api/impl/DubboTestApiImpl.java index 82655aff02..ad4d2420e6 100644 --- a/apm-agent-plugins/apm-dubbo-plugin/src/test/java/co/elastic/apm/agent/dubbo/api/impl/DubboTestApiImpl.java +++ b/apm-agent-plugins/apm-dubbo-plugin/src/test/java/co/elastic/apm/agent/dubbo/api/impl/DubboTestApiImpl.java @@ -24,7 +24,6 @@ */ package co.elastic.apm.agent.dubbo.api.impl; -import co.elastic.apm.agent.dubbo.ExecutorServiceWrapper; import co.elastic.apm.agent.dubbo.api.DubboTestApi; import co.elastic.apm.agent.dubbo.api.exception.BizException; import com.github.tomakehurst.wiremock.WireMockServer; @@ -64,7 +63,7 @@ public class DubboTestApiImpl implements DubboTestApi { public DubboTestApiImpl() { client = new OkHttpClient(); - executorService = new ExecutorServiceWrapper(Executors.newSingleThreadExecutor()); + executorService = Executors.newSingleThreadExecutor(); } @Override diff --git a/apm-agent-plugins/apm-java-concurrent-plugin/src/main/java/co/elastic/apm/agent/concurrent/ExecutorInstrumentation.java b/apm-agent-plugins/apm-java-concurrent-plugin/src/main/java/co/elastic/apm/agent/concurrent/ExecutorInstrumentation.java index 4704157ae2..459a37374f 100644 --- a/apm-agent-plugins/apm-java-concurrent-plugin/src/main/java/co/elastic/apm/agent/concurrent/ExecutorInstrumentation.java +++ b/apm-agent-plugins/apm-java-concurrent-plugin/src/main/java/co/elastic/apm/agent/concurrent/ExecutorInstrumentation.java @@ -25,7 +25,8 @@ package co.elastic.apm.agent.concurrent; import co.elastic.apm.agent.bci.ElasticApmInstrumentation; -import co.elastic.apm.agent.bci.VisibleForAdvice; +import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToArgument; +import co.elastic.apm.agent.util.GlobalVariables; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.NamedElement; import net.bytebuddy.description.method.MethodDescription; @@ -63,8 +64,7 @@ public abstract class ExecutorInstrumentation extends ElasticApmInstrumentation { - @VisibleForAdvice - public static final Set excludedClasses = new HashSet<>(); + static final Set excludedClasses = GlobalVariables.get(ExecutorInstrumentation.class, "excludedClasses", new HashSet()); static { // Used in Tomcat 7 @@ -87,6 +87,8 @@ public ElementMatcher getTypeMatcher() { return hasSuperType(named("java.util.concurrent.Executor")) // executes on same thread, no need to wrap to activate again .and(not(named("org.apache.felix.resolver.ResolverImpl$DumbExecutor"))) + .and(not(nameEndsWith("org.eclipse.jetty.util.thread.QueuedThreadPool"))) + .and(not(nameContains(".netty."))) // hazelcast tries to serialize the Runnables/Callables to execute them on remote JVMs .and(not(nameStartsWith("com.hazelcast"))) .and(not(isProxy())); @@ -97,24 +99,30 @@ public Collection getInstrumentationGroupNames() { return Arrays.asList("concurrent", "executor"); } - @VisibleForAdvice - public static boolean isExcluded(@Advice.This Executor executor) { + @Override + public boolean indyPlugin() { + return true; + } + + private static boolean isExcluded(@Advice.This Executor executor) { return excludedClasses.contains(executor.getClass().getName()); } public static class ExecutorRunnableInstrumentation extends ExecutorInstrumentation { - @Advice.OnMethodEnter(suppress = Throwable.class) - public static void onExecute(@Advice.This Executor thiz, - @Advice.Argument(value = 0, readOnly = false) @Nullable Runnable runnable) { + @Nullable + @AssignToArgument(0) + @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) + public static Runnable onExecute(@Advice.This Executor thiz, + @Advice.Argument(0) @Nullable Runnable runnable) { if (ExecutorInstrumentation.isExcluded(thiz)) { - return; + return runnable; } - runnable = JavaConcurrent.withContext(runnable, tracer); + return JavaConcurrent.withContext(runnable, tracer); } - @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) - private static void onExit(@Nullable @Advice.Thrown Throwable thrown, + @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class, inline = false) + public static void onExit(@Nullable @Advice.Thrown Throwable thrown, @Advice.Argument(value = 0) @Nullable Runnable runnable) { JavaConcurrent.doFinally(thrown, runnable); } @@ -130,26 +138,28 @@ private static void onExit(@Nullable @Advice.Thrown Throwable thrown, @Override public ElementMatcher getMethodMatcher() { return named("execute").and(returns(void.class)).and(takesArguments(Runnable.class)) - .or(named("submit").and(returns(Future.class)).and(takesArguments(Runnable.class))) - .or(named("submit").and(returns(Future.class)).and(takesArguments(Runnable.class, Object.class))) + .or(named("submit").and(returns(hasSuperType(is(Future.class)))).and(takesArguments(Runnable.class))) + .or(named("submit").and(returns(hasSuperType(is(Future.class)))).and(takesArguments(Runnable.class, Object.class))) .or(named("schedule").and(returns(ScheduledFuture.class)).and(takesArguments(Runnable.class, long.class, TimeUnit.class))); } } public static class ExecutorCallableInstrumentation extends ExecutorInstrumentation { - @Advice.OnMethodEnter(suppress = Throwable.class) - public static void onSubmit(@Advice.This Executor thiz, - @Advice.Argument(value = 0, readOnly = false) @Nullable Callable callable) { + @Nullable + @AssignToArgument(0) + @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) + public static Callable onSubmit(@Advice.This Executor thiz, + @Advice.Argument(0) @Nullable Callable callable) { if (ExecutorInstrumentation.isExcluded(thiz)) { - return; + return callable; } - callable = JavaConcurrent.withContext(callable, tracer); + return JavaConcurrent.withContext(callable, tracer); } - @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) - private static void onExit(@Nullable @Advice.Thrown Throwable thrown, - @Advice.Argument(value = 0) @Nullable Callable callable) { + @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class, inline = false) + public static void onExit(@Nullable @Advice.Thrown Throwable thrown, + @Advice.Argument(0) @Nullable Callable callable) { JavaConcurrent.doFinally(thrown, callable); } @@ -161,7 +171,7 @@ private static void onExit(@Nullable @Advice.Thrown Throwable thrown, */ @Override public ElementMatcher getMethodMatcher() { - return named("submit").and(returns(Future.class)).and(takesArguments(Callable.class)) + return named("submit").and(returns(hasSuperType(is(Future.class)))).and(takesArguments(Callable.class)) .or(named("schedule").and(returns(ScheduledFuture.class)).and(takesArguments(Callable.class, long.class, TimeUnit.class))); } @@ -184,17 +194,19 @@ public ElementMatcher getMethodMatcher() { .and(isOverriddenFrom(ExecutorService.class)); } - @Advice.OnMethodEnter(suppress = Throwable.class) - private static void onEnter(@Advice.This Executor thiz, - @Nullable @Advice.Argument(value = 0, readOnly = false) Collection> callables) { + @Nullable + @AssignToArgument(0) + @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) + public static Collection> onEnter(@Advice.This Executor thiz, + @Nullable @Advice.Argument(0) Collection> callables) { if (ExecutorInstrumentation.isExcluded(thiz)) { - return; + return callables; } - callables = JavaConcurrent.withContext(callables, tracer); + return JavaConcurrent.withContext(callables, tracer); } - @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) - private static void onExit(@Nullable @Advice.Thrown Throwable thrown, + @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class, inline = false) + public static void onExit(@Nullable @Advice.Thrown Throwable thrown, @Nullable @Advice.Argument(0) Collection> callables) { JavaConcurrent.doFinally(thrown, callables); } @@ -212,28 +224,12 @@ public ElementMatcher getTypeMatcher() { return hasSuperType(is(ForkJoinPool.class)).and(super.getTypeMatcher()); } - @Advice.OnMethodEnter(suppress = Throwable.class) - public static void onExecute(@Advice.This Executor thiz, - @Advice.Argument(value = 0, readOnly = false) @Nullable ForkJoinTask task) { - if (ExecutorInstrumentation.isExcluded(thiz)) { - return; - } - task = JavaConcurrent.withContext(task, tracer); - } - - @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) - private static void onExit(@Nullable @Advice.Thrown Throwable thrown, - @Advice.Argument(value = 0) @Nullable ForkJoinTask task) { - JavaConcurrent.doFinally(thrown, task); - } - /** *
    *
  • {@link ForkJoinPool#execute(ForkJoinTask)}
  • *
  • {@link ForkJoinPool#submit(ForkJoinTask)}
  • *
  • {@link ForkJoinPool#invoke(ForkJoinTask)}
  • *
- * @return */ @Override public ElementMatcher getMethodMatcher() { @@ -241,6 +237,28 @@ public ElementMatcher getMethodMatcher() { .or(named("submit").and(returns(ForkJoinTask.class)).and(takesArguments(ForkJoinTask.class))) .or(named("invoke").and(returns(Object.class)).and(takesArguments(ForkJoinTask.class))); } + + @Nullable + @AssignToArgument(0) + @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) + public static ForkJoinTask onExecute(@Advice.This Executor thiz, + @Advice.Argument(0) @Nullable ForkJoinTask task) { + if (ExecutorInstrumentation.isExcluded(thiz)) { + return task; + } + return JavaConcurrent.withContext(task, tracer); + } + + @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class, inline = false) + public static void onExit(@Nullable @Advice.Thrown Throwable thrown, + @Advice.Argument(value = 0) @Nullable ForkJoinTask task) { + JavaConcurrent.doFinally(thrown, task); + } + + @Override + public Collection getInstrumentationGroupNames() { + return Arrays.asList("concurrent", "fork-join"); + } } } diff --git a/apm-agent-plugins/apm-java-concurrent-plugin/src/main/java/co/elastic/apm/agent/concurrent/ForkJoinTaskInstrumentation.java b/apm-agent-plugins/apm-java-concurrent-plugin/src/main/java/co/elastic/apm/agent/concurrent/ForkJoinTaskInstrumentation.java new file mode 100644 index 0000000000..316f91d8e2 --- /dev/null +++ b/apm-agent-plugins/apm-java-concurrent-plugin/src/main/java/co/elastic/apm/agent/concurrent/ForkJoinTaskInstrumentation.java @@ -0,0 +1,76 @@ +/*- + * #%L + * Elastic APM Java agent + * %% + * Copyright (C) 2018 - 2020 Elastic and contributors + * %% + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * #L% + */ +package co.elastic.apm.agent.concurrent; + +import co.elastic.apm.agent.bci.ElasticApmInstrumentation; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; + +import javax.annotation.Nullable; +import java.util.Arrays; +import java.util.Collection; +import java.util.concurrent.ForkJoinTask; + +import static net.bytebuddy.matcher.ElementMatchers.is; +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.returns; + +/** + * Instruments {@link ForkJoinTask#fork()} to support parallel streams. + */ +public class ForkJoinTaskInstrumentation extends ElasticApmInstrumentation { + @Override + public ElementMatcher getTypeMatcher() { + return is(ForkJoinTask.class); + } + + @Override + public ElementMatcher getMethodMatcher() { + return named("fork").and(returns(ForkJoinTask.class)); + } + + @Override + public Collection getInstrumentationGroupNames() { + return Arrays.asList("concurrent", "fork-join"); + } + + @Override + public boolean indyPlugin() { + return true; + } + + @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) + public static void onExecute(@Advice.This ForkJoinTask thiz) { + JavaConcurrent.withContext(thiz, tracer); + } + + @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class, inline = false) + public static void onExit(@Nullable @Advice.Thrown Throwable thrown, + @Advice.This ForkJoinTask thiz) { + JavaConcurrent.doFinally(thrown, thiz); + } +} diff --git a/apm-agent-plugins/apm-java-concurrent-plugin/src/main/java/co/elastic/apm/agent/concurrent/JavaConcurrent.java b/apm-agent-plugins/apm-java-concurrent-plugin/src/main/java/co/elastic/apm/agent/concurrent/JavaConcurrent.java index fd26aff8ce..f8dd90a6a9 100644 --- a/apm-agent-plugins/apm-java-concurrent-plugin/src/main/java/co/elastic/apm/agent/concurrent/JavaConcurrent.java +++ b/apm-agent-plugins/apm-java-concurrent-plugin/src/main/java/co/elastic/apm/agent/concurrent/JavaConcurrent.java @@ -26,7 +26,6 @@ import co.elastic.apm.agent.bci.ElasticApmAgent; import co.elastic.apm.agent.bci.ElasticApmInstrumentation; -import co.elastic.apm.agent.bci.GlobalState; import co.elastic.apm.agent.collections.WeakMapSupplier; import co.elastic.apm.agent.impl.ElasticApmTracer; import co.elastic.apm.agent.impl.transaction.AbstractSpan; @@ -45,7 +44,7 @@ public class JavaConcurrent { private static final WeakConcurrentMap> contextMap = WeakMapSupplier.createMap(); private static final List> RUNNABLE_CALLABLE_FJTASK_INSTRUMENTATION = Collections. >singletonList(RunnableCallableForkJoinTaskInstrumentation.class); - private static final ThreadLocal needsContext = new ThreadLocal<>(); + static final ThreadLocal needsContext = new ThreadLocal<>(); private static void removeContext(Object o) { AbstractSpan context = contextMap.remove(o); @@ -91,12 +90,16 @@ public static Runnable withContext(@Nullable Runnable runnable, @Nullable Elasti if (isLambda(runnable)) { runnable = new RunnableLambdaWrapper(runnable); } - ElasticApmAgent.ensureInstrumented(runnable.getClass(), RUNNABLE_CALLABLE_FJTASK_INSTRUMENTATION); - contextMap.put(runnable, active); + captureContext(runnable, active); + return runnable; + } + + private static void captureContext(Object task, AbstractSpan active) { + ElasticApmAgent.ensureInstrumented(task.getClass(), RUNNABLE_CALLABLE_FJTASK_INSTRUMENTATION); + contextMap.put(task, active); active.incrementReferences(); // Do no discard branches leading to async operations so not to break span references active.setNonDiscardable(); - return runnable; } /** @@ -115,9 +118,7 @@ public static Callable withContext(@Nullable Callable callable, @Nulla if (isLambda(callable)) { callable = new CallableLambdaWrapper<>(callable); } - ElasticApmAgent.ensureInstrumented(callable.getClass(), RUNNABLE_CALLABLE_FJTASK_INSTRUMENTATION); - contextMap.put(callable, active); - active.incrementReferences(); + captureContext(callable, active); return callable; } @@ -131,9 +132,7 @@ public static ForkJoinTask withContext(@Nullable ForkJoinTask task, @N if (active == null) { return task; } - ElasticApmAgent.ensureInstrumented(task.getClass(), RUNNABLE_CALLABLE_FJTASK_INSTRUMENTATION); - contextMap.put(task, active); - active.incrementReferences(); + captureContext(task, active); return task; } diff --git a/apm-agent-plugins/apm-java-concurrent-plugin/src/main/java/co/elastic/apm/agent/concurrent/RunnableCallableForkJoinTaskInstrumentation.java b/apm-agent-plugins/apm-java-concurrent-plugin/src/main/java/co/elastic/apm/agent/concurrent/RunnableCallableForkJoinTaskInstrumentation.java index 3f524d9843..adacb0d36d 100644 --- a/apm-agent-plugins/apm-java-concurrent-plugin/src/main/java/co/elastic/apm/agent/concurrent/RunnableCallableForkJoinTaskInstrumentation.java +++ b/apm-agent-plugins/apm-java-concurrent-plugin/src/main/java/co/elastic/apm/agent/concurrent/RunnableCallableForkJoinTaskInstrumentation.java @@ -71,18 +71,22 @@ public Collection getInstrumentationGroupNames() { return Arrays.asList("concurrent", "executor"); } + @Override + public boolean indyPlugin() { + return true; + } + @Nullable - @Advice.OnMethodEnter(suppress = Throwable.class) - private static AbstractSpan onEnter(@Advice.This Object thiz) { + @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) + public static Object onEnter(@Advice.This Object thiz) { return JavaConcurrent.restoreContext(thiz, tracer); } - - @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) - private static void onExit(@Advice.Thrown Throwable thrown, - @Nullable @Advice.Enter AbstractSpan span) { - if (span != null) { - span.deactivate(); + @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class, inline = false) + public static void onExit(@Advice.Thrown Throwable thrown, + @Nullable @Advice.Enter Object context) { + if (context instanceof AbstractSpan) { + ((AbstractSpan) context).deactivate(); } } } diff --git a/apm-agent-plugins/apm-java-concurrent-plugin/src/main/java/co/elastic/apm/agent/concurrent/package-info.java b/apm-agent-plugins/apm-java-concurrent-plugin/src/main/java/co/elastic/apm/agent/concurrent/package-info.java index 03962f7d26..d4a49acc05 100644 --- a/apm-agent-plugins/apm-java-concurrent-plugin/src/main/java/co/elastic/apm/agent/concurrent/package-info.java +++ b/apm-agent-plugins/apm-java-concurrent-plugin/src/main/java/co/elastic/apm/agent/concurrent/package-info.java @@ -11,9 +11,9 @@ * the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -22,6 +22,10 @@ * under the License. * #L% */ +/** + * This whole plugin is not loaded from a plugin class loader as the types it uses are available form the bootstrap classloader. + * This also makes it less weird to be used as a dependency in other plugins. + */ @NonnullApi package co.elastic.apm.agent.concurrent; diff --git a/apm-agent-plugins/apm-java-concurrent-plugin/src/main/resources/META-INF/services/co.elastic.apm.agent.bci.ElasticApmInstrumentation b/apm-agent-plugins/apm-java-concurrent-plugin/src/main/resources/META-INF/services/co.elastic.apm.agent.bci.ElasticApmInstrumentation index 9f4a511a90..83b37c2043 100644 --- a/apm-agent-plugins/apm-java-concurrent-plugin/src/main/resources/META-INF/services/co.elastic.apm.agent.bci.ElasticApmInstrumentation +++ b/apm-agent-plugins/apm-java-concurrent-plugin/src/main/resources/META-INF/services/co.elastic.apm.agent.bci.ElasticApmInstrumentation @@ -2,3 +2,4 @@ co.elastic.apm.agent.concurrent.ExecutorInstrumentation$ExecutorRunnableInstrume co.elastic.apm.agent.concurrent.ExecutorInstrumentation$ExecutorCallableInstrumentation co.elastic.apm.agent.concurrent.ExecutorInstrumentation$ExecutorInvokeAnyAllInstrumentation co.elastic.apm.agent.concurrent.ExecutorInstrumentation$ForkJoinPoolInstrumentation +co.elastic.apm.agent.concurrent.ForkJoinTaskInstrumentation diff --git a/apm-agent-plugins/apm-java-concurrent-plugin/src/test/java/co/elastic/apm/agent/concurrent/AsyncTraceMethodInstrumentationTest.java b/apm-agent-plugins/apm-java-concurrent-plugin/src/test/java/co/elastic/apm/agent/concurrent/AsyncTraceMethodInstrumentationTest.java index ed164c4b3b..43ff6bfbe3 100644 --- a/apm-agent-plugins/apm-java-concurrent-plugin/src/test/java/co/elastic/apm/agent/concurrent/AsyncTraceMethodInstrumentationTest.java +++ b/apm-agent-plugins/apm-java-concurrent-plugin/src/test/java/co/elastic/apm/agent/concurrent/AsyncTraceMethodInstrumentationTest.java @@ -154,7 +154,7 @@ private void blockingMethodOnMainThread() { } private Future nonBlockingMethodOnMainThread() { - return ExecutorServiceWrapper.wrap(Executors.newFixedThreadPool(1)).submit(TestAsyncTraceMethodsClass.this::methodOnWorkerThread); + return Executors.newFixedThreadPool(1).submit(TestAsyncTraceMethodsClass.this::methodOnWorkerThread); } private void methodOnWorkerThread() { diff --git a/apm-agent-plugins/apm-java-concurrent-plugin/src/test/java/co/elastic/apm/agent/concurrent/ExcludedExecutorClassTest.java b/apm-agent-plugins/apm-java-concurrent-plugin/src/test/java/co/elastic/apm/agent/concurrent/ExcludedExecutorClassTest.java index 7cd088dafb..d0011a51c1 100644 --- a/apm-agent-plugins/apm-java-concurrent-plugin/src/test/java/co/elastic/apm/agent/concurrent/ExcludedExecutorClassTest.java +++ b/apm-agent-plugins/apm-java-concurrent-plugin/src/test/java/co/elastic/apm/agent/concurrent/ExcludedExecutorClassTest.java @@ -42,15 +42,15 @@ public class ExcludedExecutorClassTest extends AbstractInstrumentationTest { @Before public void setUp() { - executor = new ExecutorServiceWrapper(Executors.newFixedThreadPool(1)); - ExecutorInstrumentation.excludedClasses.add(ExecutorServiceWrapper.class.getName()); + executor = Executors.newFixedThreadPool(1); + ExecutorInstrumentation.excludedClasses.add(executor.getClass().getName()); transaction = tracer.startRootTransaction(null).withName("Transaction").activate(); } @After public void tearDown() { transaction.deactivate().end(); - ExecutorInstrumentation.excludedClasses.remove(ExecutorServiceWrapper.class.getName()); + ExecutorInstrumentation.excludedClasses.remove(executor.getClass().getName()); } @Test diff --git a/apm-agent-plugins/apm-java-concurrent-plugin/src/test/java/co/elastic/apm/agent/concurrent/ExecutorServiceDoubleWrappingTest.java b/apm-agent-plugins/apm-java-concurrent-plugin/src/test/java/co/elastic/apm/agent/concurrent/ExecutorServiceDoubleWrappingTest.java index 55ea7283da..7ce8509cf8 100644 --- a/apm-agent-plugins/apm-java-concurrent-plugin/src/test/java/co/elastic/apm/agent/concurrent/ExecutorServiceDoubleWrappingTest.java +++ b/apm-agent-plugins/apm-java-concurrent-plugin/src/test/java/co/elastic/apm/agent/concurrent/ExecutorServiceDoubleWrappingTest.java @@ -32,6 +32,7 @@ import org.junit.Test; import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; @@ -40,7 +41,7 @@ public class ExecutorServiceDoubleWrappingTest extends AbstractInstrumentationTest { private static final Object TEST_OBJECT = new Object(); - private final RunnableWrapperExecutorService executor = RunnableWrapperExecutorService.wrap(ExecutorServiceWrapper.wrap(Executors.newSingleThreadExecutor()), tracer); + private final ExecutorService executor = Executors.newSingleThreadExecutor(); private Transaction transaction; @Before diff --git a/apm-agent-plugins/apm-java-concurrent-plugin/src/test/java/co/elastic/apm/agent/concurrent/ExecutorServiceInstrumentationTest.java b/apm-agent-plugins/apm-java-concurrent-plugin/src/test/java/co/elastic/apm/agent/concurrent/ExecutorServiceInstrumentationTest.java index 10da15b3f8..19c9033635 100644 --- a/apm-agent-plugins/apm-java-concurrent-plugin/src/test/java/co/elastic/apm/agent/concurrent/ExecutorServiceInstrumentationTest.java +++ b/apm-agent-plugins/apm-java-concurrent-plugin/src/test/java/co/elastic/apm/agent/concurrent/ExecutorServiceInstrumentationTest.java @@ -27,7 +27,6 @@ import co.elastic.apm.agent.AbstractInstrumentationTest; import co.elastic.apm.agent.impl.transaction.Span; import co.elastic.apm.agent.impl.transaction.Transaction; -import io.netty.util.concurrent.GlobalEventExecutor; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -61,10 +60,10 @@ public ExecutorServiceInstrumentationTest(Supplier supplier) { @Parameterized.Parameters() public static Iterable> data() { - return Arrays.asList(() -> ExecutorServiceWrapper.wrap(Executors.newSingleThreadExecutor()), - () -> ExecutorServiceWrapper.wrap(Executors.newSingleThreadScheduledExecutor()), - () -> ExecutorServiceWrapper.wrap(new ForkJoinPool()), - () -> ExecutorServiceWrapper.wrap(GlobalEventExecutor.INSTANCE)); + return Arrays.asList(Executors::newSingleThreadExecutor, + Executors::newSingleThreadScheduledExecutor, + ForkJoinPool::new + ); } @Before @@ -75,7 +74,8 @@ public void setUp() { @After public void tearDown() { - assertThat(tracer.getActive()).isNull(); + transaction.deactivate().end(); + assertThat(JavaConcurrent.needsContext.get()).isNotEqualTo(false); } @Test @@ -87,25 +87,25 @@ public void run() { } }).get(); - assertAsyncSpans(1); + reporter.awaitSpanCount(1); } @Test public void testExecutorSubmitRunnableLambda() throws Exception { executor.submit(() -> createAsyncSpan()).get(1, TimeUnit.SECONDS); - assertAsyncSpans(1); + reporter.awaitSpanCount(1); } @Test public void testExecutorExecute() throws Exception { executor.execute(this::createAsyncSpan); - assertAsyncSpans(1); + reporter.awaitSpanCount(1); } @Test public void testExecutorSubmitRunnableWithResult() throws Exception { executor.submit(this::createAsyncSpan, null); - assertAsyncSpans(1); + reporter.awaitSpanCount(1); } @Test @@ -114,7 +114,7 @@ public void testExecutorSubmitCallableMethodReference() throws Exception { createAsyncSpan(); return null; }).get(1, TimeUnit.SECONDS); - assertAsyncSpans(1); + reporter.awaitSpanCount(1); } @Test @@ -126,13 +126,13 @@ public Span call() throws Exception { } })); futures.forEach(ThrowingConsumer.of(Future::get)); - assertAsyncSpans(3); + reporter.awaitSpanCount(3); } @Test public void testNestedExecutions() throws Exception { currentThreadExecutor.execute(() -> executor.execute(this::createAsyncSpan)); - assertAsyncSpans(1); + reporter.awaitSpanCount(1); } @Test @@ -151,7 +151,7 @@ public Span call() throws Exception { } }), 1, TimeUnit.SECONDS); futures.forEach(ThrowingConsumer.of(Future::get)); - assertAsyncSpans(2); + reporter.awaitSpanCount(2); } @Test @@ -162,7 +162,7 @@ public Span call() { return createAsyncSpan(); } })); - assertAsyncSpans(1); + reporter.awaitSpanCount(1); } @Test @@ -173,7 +173,7 @@ public Span call() { return createAsyncSpan(); } }), 1, TimeUnit.SECONDS); - assertAsyncSpans(1); + reporter.awaitSpanCount(1); } @FunctionalInterface @@ -190,19 +190,6 @@ static Consumer of(ThrowingConsumer throwingConsumer) { void accept(T t) throws Exception; } - - private void assertAsyncSpans(int expectedSpans) throws InterruptedException { - try { - // wait for the async operation to end - assertThat(reporter.getFirstSpan(1000)).isNotNull(); - } finally { - transaction.deactivate().end(); - } - assertThat(reporter.getTransactions()).hasSize(1); - assertThat(reporter.getSpans()).hasSize(expectedSpans); - reporter.getSpans().forEach(span -> assertThat(span.isChildOf(reporter.getFirstTransaction())).isTrue()); - } - private Span createAsyncSpan() { assertThat(tracer.getActive()).isNotNull(); assertThat(tracer.getActive().getTraceContext().getId()).isEqualTo(transaction.getTraceContext().getId()); diff --git a/apm-agent-plugins/apm-java-concurrent-plugin/src/test/java/co/elastic/apm/agent/concurrent/ExecutorServiceWrapper.java b/apm-agent-plugins/apm-java-concurrent-plugin/src/test/java/co/elastic/apm/agent/concurrent/ExecutorServiceWrapper.java deleted file mode 100644 index 7096a75251..0000000000 --- a/apm-agent-plugins/apm-java-concurrent-plugin/src/test/java/co/elastic/apm/agent/concurrent/ExecutorServiceWrapper.java +++ /dev/null @@ -1,120 +0,0 @@ -/*- - * #%L - * Elastic APM Java agent - * %% - * Copyright (C) 2018 - 2020 Elastic and contributors - * %% - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * #L% - */ -package co.elastic.apm.agent.concurrent; - - -import javax.annotation.Nonnull; -import java.util.Collection; -import java.util.List; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; - -public class ExecutorServiceWrapper implements ExecutorService { - - private final ExecutorService delegate; - - public static ExecutorService wrap(ExecutorService delegate) { - return new ExecutorServiceWrapper(delegate); - } - - public ExecutorServiceWrapper(ExecutorService delegate) { - this.delegate = delegate; - } - - @Override - public void shutdown() { - delegate.shutdown(); - } - - @Override - public List shutdownNow() { - return delegate.shutdownNow(); - } - - @Override - public boolean isShutdown() { - return delegate.isShutdown(); - } - - @Override - public boolean isTerminated() { - return delegate.isTerminated(); - } - - @Override - public boolean awaitTermination(long timeout, @Nonnull TimeUnit unit) throws InterruptedException { - return delegate.awaitTermination(timeout, unit); - } - - // Runnable - - @Override - public void execute(@Nonnull final Runnable command) { - delegate.execute(command); - } - - @Override - public Future submit(@Nonnull final Runnable task) { - return delegate.submit(task); - } - - @Override - public Future submit(@Nonnull final Runnable task, T result) { - return delegate.submit(task, result); - } - - // Callable - - @Override - public Future submit(@Nonnull final Callable task) { - return delegate.submit(task); - } - - // Collection - - @Override - public List> invokeAll(@Nonnull Collection> tasks) throws InterruptedException { - return delegate.invokeAll(tasks); - } - - @Override - public List> invokeAll(@Nonnull Collection> tasks, long timeout, @Nonnull TimeUnit unit) throws InterruptedException { - return delegate.invokeAll(tasks, timeout, unit); - } - - @Override - public T invokeAny(@Nonnull Collection> tasks) throws InterruptedException, ExecutionException { - return delegate.invokeAny(tasks); - } - - @Override - public T invokeAny(@Nonnull Collection> tasks, long timeout, @Nonnull TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { - return delegate.invokeAny(tasks, timeout, unit); - } -} diff --git a/apm-agent-plugins/apm-java-concurrent-plugin/src/test/java/co/elastic/apm/agent/concurrent/ForkJoinPoolTest.java b/apm-agent-plugins/apm-java-concurrent-plugin/src/test/java/co/elastic/apm/agent/concurrent/ForkJoinPoolTest.java index 43ea776530..e931fd6acb 100644 --- a/apm-agent-plugins/apm-java-concurrent-plugin/src/test/java/co/elastic/apm/agent/concurrent/ForkJoinPoolTest.java +++ b/apm-agent-plugins/apm-java-concurrent-plugin/src/test/java/co/elastic/apm/agent/concurrent/ForkJoinPoolTest.java @@ -11,9 +11,9 @@ * the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -31,19 +31,24 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import java.util.Objects; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ForkJoinPool; import java.util.concurrent.ForkJoinTask; +import java.util.function.Supplier; +import java.util.stream.Stream; -import static co.elastic.apm.agent.concurrent.InstrumentableForkJoinPool.newTask; +import static co.elastic.apm.agent.concurrent.ForkJoinPoolTest.AdaptedSupplier.newTask; import static org.assertj.core.api.Assertions.assertThat; public class ForkJoinPoolTest extends AbstractInstrumentationTest { - private InstrumentableForkJoinPool pool; + private ForkJoinPool pool; private Transaction transaction; @BeforeEach void setUp() { - pool = new InstrumentableForkJoinPool(); + pool = new ForkJoinPool(); transaction = tracer.startRootTransaction(null).withName("transaction").activate(); } @@ -70,4 +75,59 @@ void testInvoke() throws Exception { assertThat(pool.invoke(newTask(() -> tracer.getActive()))).isEqualTo(transaction); } + @Test + void testCompletableFuture() throws Exception { + // This test fails when debugging the tests in IntelliJ and the instrumenting agent is active + // Either run in non-debug mode or go to + // Preferences | Build, Execution, Deployment | Debugger | Async Stack Traces + // and uncheck the Instrumenting Agent checkbox + assertThat(CompletableFuture + .supplyAsync(() -> Objects.requireNonNull(tracer.getActive())) + .thenApplyAsync(active -> tracer.getActive()) + .get()) + .isEqualTo(transaction); + } + + @Test + void testParallelStream() { + assertThat(Stream.of("foo", "bar", "baz") + .parallel() + .>map(s -> tracer.getActive()) + .distinct()) + .containsExactly(transaction); + } + + public static class AdaptedSupplier extends ForkJoinTask implements Runnable { + + private final Supplier supplier; + private V result; + + public static ForkJoinTask newTask(Supplier supplier) { + return new AdaptedSupplier<>(supplier); + } + + private AdaptedSupplier(Supplier supplier) { + this.supplier = supplier; + } + + @Override + public V getRawResult() { + return result; + } + + @Override + protected void setRawResult(V value) { + result = value; + } + + @Override + protected boolean exec() { + result = supplier.get(); + return true; + } + + @Override + public final void run() { invoke(); } + + } } diff --git a/apm-agent-plugins/apm-java-concurrent-plugin/src/test/java/co/elastic/apm/agent/concurrent/InstrumentableForkJoinPool.java b/apm-agent-plugins/apm-java-concurrent-plugin/src/test/java/co/elastic/apm/agent/concurrent/InstrumentableForkJoinPool.java deleted file mode 100644 index 5e5002b7a6..0000000000 --- a/apm-agent-plugins/apm-java-concurrent-plugin/src/test/java/co/elastic/apm/agent/concurrent/InstrumentableForkJoinPool.java +++ /dev/null @@ -1,114 +0,0 @@ -/*- - * #%L - * Elastic APM Java agent - * %% - * Copyright (C) 2018 - 2020 Elastic and contributors - * %% - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * #L% - */ -package co.elastic.apm.agent.concurrent; - -import java.util.Collection; -import java.util.List; -import java.util.concurrent.Callable; -import java.util.concurrent.ForkJoinPool; -import java.util.concurrent.ForkJoinTask; -import java.util.concurrent.Future; -import java.util.function.Supplier; - -/** - * We can't instrument bootstrap classes, like {@link ForkJoinPool} in unit tests currently. - * This class makes sure the relevat methods can be instrumented. - */ -public class InstrumentableForkJoinPool extends ForkJoinPool { - - public static ForkJoinTask newTask(Supplier supplier) { - return new AdaptedSupplier<>(supplier); - } - - @Override - public T invoke(ForkJoinTask task) { - return super.invoke(task); - } - - @Override - public void execute(ForkJoinTask task) { - super.execute(task); - } - - @Override - public void execute(Runnable task) { - super.execute(task); - } - - @Override - public ForkJoinTask submit(ForkJoinTask task) { - return super.submit(task); - } - - @Override - public ForkJoinTask submit(Callable task) { - return super.submit(task); - } - - @Override - public ForkJoinTask submit(Runnable task, T result) { - return super.submit(task, result); - } - - @Override - public ForkJoinTask submit(Runnable task) { - return super.submit(task); - } - - @Override - public List> invokeAll(Collection> tasks) { - return super.invokeAll(tasks); - } - - private static class AdaptedSupplier extends ForkJoinTask implements Runnable { - - private final Supplier supplier; - private V result; - - public AdaptedSupplier(Supplier supplier) { - this.supplier = supplier; - } - - @Override - public V getRawResult() { - return result; - } - - @Override - protected void setRawResult(V value) { - result = value; - } - - @Override - protected boolean exec() { - result = supplier.get(); - return true; - } - - @Override - public final void run() { invoke(); } - - } -} diff --git a/apm-agent-plugins/apm-java-concurrent-plugin/src/test/java/co/elastic/apm/agent/concurrent/RunnableWrapperExecutorService.java b/apm-agent-plugins/apm-java-concurrent-plugin/src/test/java/co/elastic/apm/agent/concurrent/RunnableWrapperExecutorService.java deleted file mode 100644 index 8fc2f79038..0000000000 --- a/apm-agent-plugins/apm-java-concurrent-plugin/src/test/java/co/elastic/apm/agent/concurrent/RunnableWrapperExecutorService.java +++ /dev/null @@ -1,85 +0,0 @@ -/*- - * #%L - * Elastic APM Java agent - * %% - * Copyright (C) 2018 - 2020 Elastic and contributors - * %% - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * #L% - */ -package co.elastic.apm.agent.concurrent; - -import co.elastic.apm.agent.impl.ElasticApmTracer; - -import javax.annotation.Nonnull; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Future; - -import static org.assertj.core.api.Assertions.assertThat; - -public class RunnableWrapperExecutorService extends ExecutorServiceWrapper { - - private final ElasticApmTracer tracer; - - public static RunnableWrapperExecutorService wrap(ExecutorService delegate, ElasticApmTracer tracer) { - return new RunnableWrapperExecutorService(delegate, tracer); - } - - private RunnableWrapperExecutorService(ExecutorService delegate, ElasticApmTracer tracer) { - super(delegate); - this.tracer = tracer; - } - - @Override - public void execute(@Nonnull Runnable command) { - super.execute(() -> { - assertThat(tracer.getActive()).isNull(); - command.run(); - assertThat(tracer.getActive()).isNull(); - }); - } - - @Override - public Future submit(@Nonnull Runnable task) { - return super.submit(() -> { - assertThat(tracer.getActive()).isNull(); - task.run(); - assertThat(tracer.getActive()).isNull(); - }); - } - - @Override - public Future submit(@Nonnull Runnable task, T result) { - return super.submit(() -> { - assertThat(tracer.getActive()).isNull(); - task.run(); - assertThat(tracer.getActive()).isNull(); - }, result); - } - - @Override - public Future submit(@Nonnull Callable task) { - return super.submit(() -> { - assertThat(tracer.getActive()).isNull(); - T ret = task.call(); - assertThat(tracer.getActive()).isNull(); - return ret; - }); - } -} diff --git a/apm-agent-plugins/apm-java-concurrent-plugin/src/test/java/co/elastic/apm/agent/concurrent/ScopeManagementTest.java b/apm-agent-plugins/apm-java-concurrent-plugin/src/test/java/co/elastic/apm/agent/concurrent/ScopeManagementTest.java index 4f62931b79..337a8dd0d7 100644 --- a/apm-agent-plugins/apm-java-concurrent-plugin/src/test/java/co/elastic/apm/agent/concurrent/ScopeManagementTest.java +++ b/apm-agent-plugins/apm-java-concurrent-plugin/src/test/java/co/elastic/apm/agent/concurrent/ScopeManagementTest.java @@ -100,7 +100,7 @@ void testSpanAndContextCallableActivation() { @Test void testContextAndSpanRunnableActivationInDifferentThread() throws Exception { final Transaction transaction = tracer.startRootTransaction(null).activate(); - ExecutorServiceWrapper.wrap(Executors.newSingleThreadExecutor()).submit(() -> { + Executors.newSingleThreadExecutor().submit(() -> { assertThat(tracer.getActive()).isSameAs(transaction); assertThat(tracer.currentTransaction()).isSameAs(transaction); }).get(); @@ -112,7 +112,7 @@ void testContextAndSpanRunnableActivationInDifferentThread() throws Exception { @Test void testContextAndSpanCallableActivationInDifferentThread() throws Exception { final Transaction transaction = tracer.startRootTransaction(null).activate(); - Future transactionFuture = ExecutorServiceWrapper.wrap(Executors.newSingleThreadExecutor()).submit(() -> { + Future transactionFuture = Executors.newSingleThreadExecutor().submit(() -> { assertThat(tracer.getActive()).isSameAs(transaction); return tracer.currentTransaction(); }); @@ -129,7 +129,7 @@ void testSpanAndContextRunnableActivationInDifferentThread() throws Exception { assertThat(tracer.currentTransaction()).isSameAs(transaction); assertThat(tracer.getActive()).isSameAs(transaction); }; - ExecutorServiceWrapper.wrap(Executors.newSingleThreadExecutor()).submit(runnable).get(); + Executors.newSingleThreadExecutor().submit(runnable).get(); transaction.deactivate(); assertThat(tracer.getActive()).isNull(); @@ -138,7 +138,7 @@ void testSpanAndContextRunnableActivationInDifferentThread() throws Exception { @Test void testSpanAndContextCallableActivationInDifferentThread() throws Exception { final Transaction transaction = tracer.startRootTransaction(null).activate(); - assertThat(ExecutorServiceWrapper.wrap(Executors.newSingleThreadExecutor()).submit(() -> { + assertThat(Executors.newSingleThreadExecutor().submit(() -> { assertThat(tracer.currentTransaction()).isSameAs(transaction); return tracer.currentTransaction(); }).get()).isSameAs(transaction); diff --git a/apm-agent-plugins/apm-jdbc-plugin/src/main/java/co/elastic/apm/agent/jdbc/StatementInstrumentation.java b/apm-agent-plugins/apm-jdbc-plugin/src/main/java/co/elastic/apm/agent/jdbc/StatementInstrumentation.java index d401d318ce..00bfcf5f02 100644 --- a/apm-agent-plugins/apm-jdbc-plugin/src/main/java/co/elastic/apm/agent/jdbc/StatementInstrumentation.java +++ b/apm-agent-plugins/apm-jdbc-plugin/src/main/java/co/elastic/apm/agent/jdbc/StatementInstrumentation.java @@ -97,8 +97,8 @@ public ExecuteWithQueryInstrumentation(ElasticApmTracer tracer) { @Nullable @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) - public static Span onBeforeExecute(@Advice.This Statement statement, - @Advice.Argument(0) String sql) { + public static Object onBeforeExecute(@Advice.This Statement statement, + @Advice.Argument(0) String sql) { if (tracer == null) { return null; @@ -110,13 +110,13 @@ public static Span onBeforeExecute(@Advice.This Statement statement, @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class, inline = false) public static void onAfterExecute(@Advice.This Statement statement, - @Advice.Enter @Nullable Span span, + @Advice.Enter @Nullable Object span, @Advice.Thrown @Nullable Throwable t) { if (span == null) { return; } - span.captureException(t) + ((Span) span).captureException(t) .deactivate() .end(); @@ -148,7 +148,7 @@ public ExecuteUpdateWithQueryInstrumentation(ElasticApmTracer tracer) { @Nullable @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) - public static Span onBeforeExecute(@Advice.This Statement statement, + public static Object onBeforeExecute(@Advice.This Statement statement, @Advice.Argument(0) String sql) { if (tracer == null) { return null; @@ -158,7 +158,7 @@ public static Span onBeforeExecute(@Advice.This Statement statement, } @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class, inline = false) - public static void onAfterExecute(@Advice.Enter @Nullable Span span, + public static void onAfterExecute(@Advice.Enter @Nullable Object span, @Advice.Thrown @Nullable Throwable t, @Advice.Return long returnValue /* bytebuddy converts int to long for us here ! */) { if (span == null) { @@ -166,12 +166,12 @@ public static void onAfterExecute(@Advice.Enter @Nullable Span span, } if (t == null) { - span.getContext() + ((Span) span).getContext() .getDb() .withAffectedRowsCount(returnValue); } - span.captureException(t) + ((Span) span).captureException(t) .deactivate() .end(); } @@ -218,7 +218,7 @@ public ExecuteBatchInstrumentation(ElasticApmTracer tracer) { @Nullable @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) @SuppressWarnings("DuplicatedCode") - public static Span onBeforeExecute(@Advice.This Statement statement) { + public static Object onBeforeExecute(@Advice.This Statement statement) { if (tracer == null) { return null; } @@ -228,7 +228,7 @@ public static Span onBeforeExecute(@Advice.This Statement statement) { } @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class, inline = false) - public static void onAfterExecute(@Advice.Enter @Nullable Span span, + public static void onAfterExecute(@Advice.Enter @Nullable Object span, @Advice.Thrown Throwable t, @Advice.Return Object returnValue) { if (span == null) { @@ -251,11 +251,11 @@ public static void onAfterExecute(@Advice.Enter @Nullable Span span, affectedCount += array[i]; } } - span.getContext() + ((Span) span).getContext() .getDb() .withAffectedRowsCount(affectedCount); - span.captureException(t) + ((Span) span).captureException(t) .deactivate() .end(); } @@ -281,7 +281,7 @@ public ExecuteUpdateNoQueryInstrumentation(ElasticApmTracer tracer) { @Nullable @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) @SuppressWarnings("DuplicatedCode") - public static Span onBeforeExecute(@Advice.This Statement statement) { + public static Object onBeforeExecute(@Advice.This Statement statement) { if (tracer == null) { return null; } @@ -291,7 +291,7 @@ public static Span onBeforeExecute(@Advice.This Statement statement) { } @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class, inline = false) - public static void onAfterExecute(@Advice.Enter @Nullable Span span, + public static void onAfterExecute(@Advice.Enter @Nullable Object span, @Advice.Thrown @Nullable Throwable t, @Advice.Return long returnValue /* bytebuddy converts int to long for us here ! */) { if (span == null) { @@ -299,12 +299,12 @@ public static void onAfterExecute(@Advice.Enter @Nullable Span span, } if (t == null) { - span.getContext() + ((Span) span).getContext() .getDb() .withAffectedRowsCount(returnValue); } - span.captureException(t) + ((Span) span).captureException(t) .deactivate() .end(); } @@ -329,7 +329,7 @@ public ExecutePreparedStatementInstrumentation(ElasticApmTracer tracer) { @Nullable @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) @SuppressWarnings("DuplicatedCode") - public static Span onBeforeExecute(@Advice.This Statement statement) { + public static Object onBeforeExecute(@Advice.This Statement statement) { if (tracer != null) { @Nullable String sql = jdbcHelper.retrieveSqlForStatement(statement); return jdbcHelper.createJdbcSpan(sql, statement, tracer.getActive(), true); @@ -339,14 +339,14 @@ public static Span onBeforeExecute(@Advice.This Statement statement) { @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class, inline = false) public static void onAfterExecute(@Advice.This Statement statement, - @Advice.Enter @Nullable Span span, + @Advice.Enter @Nullable Object span, @Advice.Thrown @Nullable Throwable t) { if (span == null) { return; } - span.captureException(t) + ((Span) span).captureException(t) .deactivate() .end(); } diff --git a/apm-agent-plugins/apm-process-plugin/src/main/java/co/elastic/apm/agent/process/BaseProcessInstrumentation.java b/apm-agent-plugins/apm-process-plugin/src/main/java/co/elastic/apm/agent/process/BaseProcessInstrumentation.java index 214a89a964..4c4e664986 100644 --- a/apm-agent-plugins/apm-process-plugin/src/main/java/co/elastic/apm/agent/process/BaseProcessInstrumentation.java +++ b/apm-agent-plugins/apm-process-plugin/src/main/java/co/elastic/apm/agent/process/BaseProcessInstrumentation.java @@ -45,4 +45,8 @@ public final Collection getInstrumentationGroupNames() { return Collections.singletonList("process"); } + @Override + public boolean indyPlugin() { + return true; + } } diff --git a/apm-agent-plugins/apm-process-plugin/src/main/java/co/elastic/apm/agent/process/CommonsExecAsyncInstrumentation.java b/apm-agent-plugins/apm-process-plugin/src/main/java/co/elastic/apm/agent/process/CommonsExecAsyncInstrumentation.java index 6207d36d72..9dce6ead9a 100644 --- a/apm-agent-plugins/apm-process-plugin/src/main/java/co/elastic/apm/agent/process/CommonsExecAsyncInstrumentation.java +++ b/apm-agent-plugins/apm-process-plugin/src/main/java/co/elastic/apm/agent/process/CommonsExecAsyncInstrumentation.java @@ -33,6 +33,7 @@ import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.matcher.ElementMatcher; +import javax.annotation.Nullable; import java.util.Arrays; import java.util.Collection; @@ -87,17 +88,23 @@ public Class getAdviceClass() { return CommonsExecAdvice.class; } + @Override + public boolean indyPlugin() { + return true; + } + public static final class CommonsExecAdvice { + @Nullable @AssignToArgument(0) - @Advice.OnMethodEnter(suppress = Throwable.class) - private static Runnable onEnter(Runnable runnable) { + @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) + public static Runnable onEnter(Runnable runnable) { return JavaConcurrent.withContext(runnable, tracer); } - @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) - private static void onExit(@Advice.Thrown Throwable thrown, - @Advice.Argument(value = 0) Runnable runnable) { + @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class, inline = false) + public static void onExit(@Advice.Thrown Throwable thrown, + @Advice.Argument(value = 0) Runnable runnable) { JavaConcurrent.doFinally(thrown, runnable); } } diff --git a/apm-agent-plugins/apm-process-plugin/src/main/java/co/elastic/apm/agent/process/ProcessExitInstrumentation.java b/apm-agent-plugins/apm-process-plugin/src/main/java/co/elastic/apm/agent/process/ProcessExitInstrumentation.java index 2f93c087ec..e00f03e218 100644 --- a/apm-agent-plugins/apm-process-plugin/src/main/java/co/elastic/apm/agent/process/ProcessExitInstrumentation.java +++ b/apm-agent-plugins/apm-process-plugin/src/main/java/co/elastic/apm/agent/process/ProcessExitInstrumentation.java @@ -11,9 +11,9 @@ * the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -71,8 +71,8 @@ public Class getAdviceClass() { public static class WaitForAdvice { - @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) - private static void onExit(@Advice.This Process process) { + @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class, inline = false) + public static void onExit(@Advice.This Process process) { if (tracer == null || tracer.getActive() == null) { return; @@ -109,8 +109,8 @@ public Class getAdviceClass() { public static class DestroyAdvice { - @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) - private static void onExit(@Advice.This Process process) { + @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class, inline = false) + public static void onExit(@Advice.This Process process) { if (tracer == null || tracer.getActive() == null) { return; diff --git a/apm-agent-plugins/apm-process-plugin/src/main/java/co/elastic/apm/agent/process/ProcessHelper.java b/apm-agent-plugins/apm-process-plugin/src/main/java/co/elastic/apm/agent/process/ProcessHelper.java index 79fff89a73..042cd40023 100644 --- a/apm-agent-plugins/apm-process-plugin/src/main/java/co/elastic/apm/agent/process/ProcessHelper.java +++ b/apm-agent-plugins/apm-process-plugin/src/main/java/co/elastic/apm/agent/process/ProcessHelper.java @@ -24,7 +24,6 @@ */ package co.elastic.apm.agent.process; -import co.elastic.apm.agent.bci.VisibleForAdvice; import co.elastic.apm.agent.collections.WeakMapSupplier; import co.elastic.apm.agent.impl.transaction.AbstractSpan; import co.elastic.apm.agent.impl.transaction.Span; @@ -38,8 +37,7 @@ * Having direct references to {@link Process} class is safe here because those are loaded in the bootstrap classloader. * Thus there is no need to separate helper interface from implementation or use {@link co.elastic.apm.agent.bci.HelperClassManager}. */ -@VisibleForAdvice -public class ProcessHelper { +class ProcessHelper { private static final ProcessHelper INSTANCE = new ProcessHelper(WeakMapSupplier.createMap()); @@ -49,13 +47,11 @@ public class ProcessHelper { this.inFlightSpans = inFlightSpans; } - @VisibleForAdvice - public static void startProcess(AbstractSpan parentContext, Process process, List command) { + static void startProcess(AbstractSpan parentContext, Process process, List command) { INSTANCE.doStartProcess(parentContext, process, command.get(0)); } - @VisibleForAdvice - public static void endProcess(@Nonnull Process process, boolean checkTerminatedProcess) { + static void endProcess(@Nonnull Process process, boolean checkTerminatedProcess) { INSTANCE.doEndProcess(process, checkTerminatedProcess); } diff --git a/apm-agent-plugins/apm-process-plugin/src/main/java/co/elastic/apm/agent/process/ProcessStartInstrumentation.java b/apm-agent-plugins/apm-process-plugin/src/main/java/co/elastic/apm/agent/process/ProcessStartInstrumentation.java index 50e9e3d144..5c7134e936 100644 --- a/apm-agent-plugins/apm-process-plugin/src/main/java/co/elastic/apm/agent/process/ProcessStartInstrumentation.java +++ b/apm-agent-plugins/apm-process-plugin/src/main/java/co/elastic/apm/agent/process/ProcessStartInstrumentation.java @@ -58,7 +58,7 @@ public Class getAdviceClass() { public static class ProcessBuilderStartAdvice { - @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) + @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class, inline = false) public static void onExit(@Advice.This ProcessBuilder processBuilder, @Advice.Return Process process, @Advice.Thrown @Nullable Throwable t) { diff --git a/apm-agent-plugins/apm-urlconnection-plugin/src/main/java/co/elastic/apm/agent/urlconnection/HttpUrlConnectionInstrumentation.java b/apm-agent-plugins/apm-urlconnection-plugin/src/main/java/co/elastic/apm/agent/urlconnection/HttpUrlConnectionInstrumentation.java index 7508d4c50a..554206c459 100644 --- a/apm-agent-plugins/apm-urlconnection-plugin/src/main/java/co/elastic/apm/agent/urlconnection/HttpUrlConnectionInstrumentation.java +++ b/apm-agent-plugins/apm-urlconnection-plugin/src/main/java/co/elastic/apm/agent/urlconnection/HttpUrlConnectionInstrumentation.java @@ -69,17 +69,22 @@ public ElementMatcher getTypeMatcher() { return hasSuperType(is(HttpURLConnection.class)); } + @Override + public boolean indyPlugin() { + return true; + } + public static class CreateSpanInstrumentation extends HttpUrlConnectionInstrumentation { - @Advice.OnMethodEnter(suppress = Throwable.class) - public static void enter(@Advice.This HttpURLConnection thiz, + @Nullable + @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) + public static Object enter(@Advice.This HttpURLConnection thiz, @Advice.FieldValue("connected") boolean connected, - @Advice.Local("span") Span span, @Advice.Origin String signature) { if (tracer == null || tracer.getActive() == null) { - return; + return null; } - span = inFlightSpans.get(thiz); + Span span = inFlightSpans.get(thiz); if (span == null && !connected) { final URL url = thiz.getURL(); span = HttpClientHelper.startHttpClientSpan(tracer.getActive(), thiz.getRequestMethod(), url.toString(), url.getProtocol(), url.getHost(), url.getPort()); @@ -92,14 +97,16 @@ public static void enter(@Advice.This HttpURLConnection thiz, if (span != null) { span.activate(); } + return span; } - @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) + @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class, inline = false) public static void exit(@Advice.This HttpURLConnection thiz, @Nullable @Advice.Thrown Throwable t, @Advice.FieldValue("responseCode") int responseCode, - @Nullable @Advice.Local("span") Span span, + @Nullable @Advice.Enter Object spanObject, @Advice.Origin String signature) { + Span span = (Span) spanObject; if (span == null) { return; } @@ -135,7 +142,7 @@ public ElementMatcher getMethodMatcher() { */ public static class DisconnectInstrumentation extends HttpUrlConnectionInstrumentation { - @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) + @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class, inline = false) public static void afterDisconnect(@Advice.This HttpURLConnection thiz, @Nullable @Advice.Thrown Throwable t, @Advice.FieldValue("responseCode") int responseCode) { diff --git a/apm-agent-plugins/apm-urlconnection-plugin/src/main/java/co/elastic/apm/agent/urlconnection/SSLContextInstrumentation.java b/apm-agent-plugins/apm-urlconnection-plugin/src/main/java/co/elastic/apm/agent/urlconnection/SSLContextInstrumentation.java index 41514de245..04cc7e5887 100644 --- a/apm-agent-plugins/apm-urlconnection-plugin/src/main/java/co/elastic/apm/agent/urlconnection/SSLContextInstrumentation.java +++ b/apm-agent-plugins/apm-urlconnection-plugin/src/main/java/co/elastic/apm/agent/urlconnection/SSLContextInstrumentation.java @@ -77,10 +77,15 @@ public Collection getInstrumentationGroupNames() { return Collections.singleton("ssl-context"); } + @Override + public boolean indyPlugin() { + return true; + } + /** * This will not allow using the default SSL factory from any agent thread */ - @Advice.OnMethodEnter(suppress = Throwable.class, skipOn = Advice.OnNonDefaultValue.class) + @Advice.OnMethodEnter(suppress = Throwable.class, skipOn = Advice.OnNonDefaultValue.class, inline = false) public static boolean skipExecutionIfAgentThread() { return Thread.currentThread().getName().startsWith(ThreadUtils.ELASTIC_APM_THREAD_PREFIX); } diff --git a/apm-agent-plugins/apm-urlconnection-plugin/src/test/java/co/elastic/apm/agent/urlconnection/HttpUrlConnectionInstrumentationTest.java b/apm-agent-plugins/apm-urlconnection-plugin/src/test/java/co/elastic/apm/agent/urlconnection/HttpUrlConnectionInstrumentationTest.java index 8efb795443..dbb77333ab 100644 --- a/apm-agent-plugins/apm-urlconnection-plugin/src/test/java/co/elastic/apm/agent/urlconnection/HttpUrlConnectionInstrumentationTest.java +++ b/apm-agent-plugins/apm-urlconnection-plugin/src/test/java/co/elastic/apm/agent/urlconnection/HttpUrlConnectionInstrumentationTest.java @@ -31,17 +31,8 @@ import org.junit.Test; import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.lang.reflect.Field; -import java.net.Authenticator; import java.net.HttpURLConnection; -import java.net.ProtocolException; import java.net.URL; -import java.net.URLConnection; -import java.security.Permission; -import java.util.List; -import java.util.Map; import static com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor; import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; @@ -52,14 +43,14 @@ public class HttpUrlConnectionInstrumentationTest extends AbstractHttpClientInst @Override protected void performGet(String path) throws Exception { - final HttpURLConnection urlConnection = new HttpURLConnectionWrapper(new HttpURLConnectionWrapper((HttpURLConnection) new URL(path).openConnection())); + final HttpURLConnection urlConnection = (HttpURLConnection) new URL(path).openConnection(); urlConnection.getInputStream(); urlConnection.disconnect(); } @Test public void testEndInDifferentThread() throws Exception { - final HttpURLConnection urlConnection = new HttpURLConnectionWrapper(new HttpURLConnectionWrapper((HttpURLConnection) new URL(getBaseUrl() + "/").openConnection())); + final HttpURLConnection urlConnection = (HttpURLConnection) new URL(getBaseUrl() + "/").openConnection(); urlConnection.connect(); AbstractSpan active = tracer.getActive(); final Thread thread = new Thread(() -> { @@ -77,7 +68,7 @@ public void testEndInDifferentThread() throws Exception { @Test public void testDisconnectionWithoutExecute() throws Exception { - final HttpURLConnection urlConnection = new HttpURLConnectionWrapper(new HttpURLConnectionWrapper((HttpURLConnection) new URL(getBaseUrl() + "/").openConnection())); + final HttpURLConnection urlConnection = (HttpURLConnection) new URL(getBaseUrl() + "/").openConnection(); urlConnection.connect(); urlConnection.disconnect(); assertThat(reporter.getSpans()).hasSize(1); @@ -86,7 +77,7 @@ public void testDisconnectionWithoutExecute() throws Exception { @Test public void testMultipleConnect() throws Exception { - final HttpURLConnection urlConnection = new HttpURLConnectionWrapper(new HttpURLConnectionWrapper((HttpURLConnection) new URL(getBaseUrl() + "/").openConnection())); + final HttpURLConnection urlConnection = (HttpURLConnection) new URL(getBaseUrl() + "/").openConnection(); urlConnection.connect(); urlConnection.connect(); urlConnection.getInputStream(); @@ -96,7 +87,7 @@ public void testMultipleConnect() throws Exception { @Test public void testGetOutputStream() throws Exception { - final HttpURLConnection urlConnection = new HttpURLConnectionWrapper(new HttpURLConnectionWrapper((HttpURLConnection) new URL(getBaseUrl() + "/").openConnection())); + final HttpURLConnection urlConnection = (HttpURLConnection) new URL(getBaseUrl() + "/").openConnection(); urlConnection.setDoOutput(true); urlConnection.getOutputStream(); urlConnection.getInputStream(); @@ -106,7 +97,7 @@ public void testGetOutputStream() throws Exception { @Test public void testConnectAfterInputStream() throws Exception { - final HttpURLConnection urlConnection = new HttpURLConnectionWrapper(new HttpURLConnectionWrapper((HttpURLConnection) new URL(getBaseUrl() + "/").openConnection())); + final HttpURLConnection urlConnection = (HttpURLConnection) new URL(getBaseUrl() + "/").openConnection(); urlConnection.getInputStream(); // should not create another span // works because the connected flag is checked @@ -119,7 +110,7 @@ public void testConnectAfterInputStream() throws Exception { @Test @Ignore public void testFakeReuse() throws Exception { - final HttpURLConnection urlConnection = new HttpURLConnectionWrapper(new HttpURLConnectionWrapper((HttpURLConnection) new URL(getBaseUrl() + "/").openConnection())); + final HttpURLConnection urlConnection = (HttpURLConnection) new URL(getBaseUrl() + "/").openConnection(); urlConnection.getInputStream(); urlConnection.disconnect(); @@ -136,346 +127,4 @@ public void testFakeReuse() throws Exception { verifyHttpSpan("/"); } - // The actual HttpURLConnection is loaded by the bootstrap class loader which we can't instrument in tests - // That's why we wrap the connection into a wrapper which actually gets instrumented. - public static class HttpURLConnectionWrapper extends HttpURLConnection { - - private static final Field CONNECTED; - private static final Field RESPONSE_CODE; - - static { - try { - CONNECTED = URLConnection.class.getDeclaredField("connected"); - CONNECTED.setAccessible(true); - RESPONSE_CODE = HttpURLConnection.class.getDeclaredField("responseCode"); - RESPONSE_CODE.setAccessible(true); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - private final HttpURLConnection delegate; - - HttpURLConnectionWrapper(HttpURLConnection delegate) { - super(delegate.getURL()); - this.delegate = delegate; - } - - private void updateFields() { - connected = getConnectedFromDelegate(); - responseCode = getResponseCodeFromDelegate(); - } - - private boolean getConnectedFromDelegate() { - try { - return (Boolean) CONNECTED.get(delegate); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - private int getResponseCodeFromDelegate() { - try { - return (Integer) RESPONSE_CODE.get(delegate); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - @Override - public void connect() throws IOException { - try { - delegate.connect(); - } finally { - updateFields(); - } - } - - @Override - public InputStream getInputStream() throws IOException { - try { - return delegate.getInputStream(); - } finally { - updateFields(); - } - } - - @Override - public OutputStream getOutputStream() throws IOException { - try { - return delegate.getOutputStream(); - } finally { - updateFields(); - } - } - - @Override - public void disconnect() { - try { - delegate.disconnect(); - } finally { - updateFields(); - } - } - - @Override - public int getResponseCode() throws IOException { - try { - return delegate.getResponseCode(); - } finally { - updateFields(); - } - } - - @Override - public boolean usingProxy() { - return delegate.usingProxy(); - } - - @Override - public int getConnectTimeout() { - return delegate.getConnectTimeout(); - } - - @Override - public void setConnectTimeout(int timeout) { - delegate.setConnectTimeout(timeout); - } - - @Override - public int getReadTimeout() { - return delegate.getReadTimeout(); - } - - @Override - public void setReadTimeout(int timeout) { - delegate.setReadTimeout(timeout); - } - - @Override - public URL getURL() { - return delegate.getURL(); - } - - @Override - public int getContentLength() { - return delegate.getContentLength(); - } - - @Override - public long getContentLengthLong() { - return delegate.getContentLengthLong(); - } - - @Override - public String getContentType() { - return delegate.getContentType(); - } - - @Override - public String getContentEncoding() { - return delegate.getContentEncoding(); - } - - @Override - public long getExpiration() { - return delegate.getExpiration(); - } - - @Override - public long getDate() { - return delegate.getDate(); - } - - @Override - public long getLastModified() { - return delegate.getLastModified(); - } - - @Override - public String getHeaderField(String name) { - return delegate.getHeaderField(name); - } - - @Override - public Map> getHeaderFields() { - return delegate.getHeaderFields(); - } - - @Override - public int getHeaderFieldInt(String name, int Default) { - return delegate.getHeaderFieldInt(name, Default); - } - - @Override - public long getHeaderFieldLong(String name, long Default) { - return delegate.getHeaderFieldLong(name, Default); - } - - @Override - public Object getContent() throws IOException { - return delegate.getContent(); - } - - @Override - public Object getContent(Class[] classes) throws IOException { - return delegate.getContent(classes); - } - - @Override - public String toString() { - return delegate.toString(); - } - - @Override - public boolean getDoInput() { - return delegate.getDoInput(); - } - - @Override - public void setDoInput(boolean doinput) { - delegate.setDoInput(doinput); - } - - @Override - public boolean getDoOutput() { - return delegate.getDoOutput(); - } - - @Override - public void setDoOutput(boolean dooutput) { - delegate.setDoOutput(dooutput); - } - - @Override - public boolean getAllowUserInteraction() { - return delegate.getAllowUserInteraction(); - } - - @Override - public void setAllowUserInteraction(boolean allowuserinteraction) { - delegate.setAllowUserInteraction(allowuserinteraction); - } - - @Override - public boolean getUseCaches() { - return delegate.getUseCaches(); - } - - @Override - public void setUseCaches(boolean usecaches) { - delegate.setUseCaches(usecaches); - } - - @Override - public long getIfModifiedSince() { - return delegate.getIfModifiedSince(); - } - - @Override - public void setIfModifiedSince(long ifmodifiedsince) { - delegate.setIfModifiedSince(ifmodifiedsince); - } - - @Override - public boolean getDefaultUseCaches() { - return delegate.getDefaultUseCaches(); - } - - @Override - public void setDefaultUseCaches(boolean defaultusecaches) { - delegate.setDefaultUseCaches(defaultusecaches); - } - - @Override - public void setRequestProperty(String key, String value) { - delegate.setRequestProperty(key, value); - } - - @Override - public void addRequestProperty(String key, String value) { - delegate.addRequestProperty(key, value); - } - - @Override - public String getRequestProperty(String key) { - return delegate.getRequestProperty(key); - } - - @Override - public Map> getRequestProperties() { - return delegate.getRequestProperties(); - } - - @Override - public void setAuthenticator(Authenticator auth) { - delegate.setAuthenticator(auth); - } - - @Override - public String getHeaderFieldKey(int n) { - return delegate.getHeaderFieldKey(n); - } - - @Override - public void setFixedLengthStreamingMode(int contentLength) { - delegate.setFixedLengthStreamingMode(contentLength); - } - - @Override - public void setFixedLengthStreamingMode(long contentLength) { - delegate.setFixedLengthStreamingMode(contentLength); - } - - @Override - public void setChunkedStreamingMode(int chunklen) { - delegate.setChunkedStreamingMode(chunklen); - } - - @Override - public String getHeaderField(int n) { - return delegate.getHeaderField(n); - } - - @Override - public boolean getInstanceFollowRedirects() { - return delegate.getInstanceFollowRedirects(); - } - - @Override - public void setInstanceFollowRedirects(boolean followRedirects) { - delegate.setInstanceFollowRedirects(followRedirects); - } - - @Override - public String getRequestMethod() { - return delegate.getRequestMethod(); - } - - @Override - public void setRequestMethod(String method) throws ProtocolException { - delegate.setRequestMethod(method); - } - - @Override - public String getResponseMessage() throws IOException { - return delegate.getResponseMessage(); - } - - @Override - public long getHeaderFieldDate(String name, long Default) { - return delegate.getHeaderFieldDate(name, Default); - } - - @Override - public Permission getPermission() throws IOException { - return delegate.getPermission(); - } - - @Override - public InputStream getErrorStream() { - return delegate.getErrorStream(); - } - - } } From 8f156cfedc21c08a4c9fbeb358db71230fbc2a86 Mon Sep 17 00:00:00 2001 From: Felix Barnsteiner Date: Thu, 18 Jun 2020 14:31:27 +0200 Subject: [PATCH 21/35] Make @AssignTo* annotations inner classes of @AssignTo --- .../bci/bytebuddy/postprocessor/AssignTo.java | 130 +++++++++++++++++- .../postprocessor/AssignToArgument.java | 58 -------- .../postprocessor/AssignToField.java | 68 --------- .../AssignToPostProcessorFactory.java | 32 ++--- .../postprocessor/AssignToReturn.java | 51 ------- .../bytebuddy/postprocessor/package-info.java | 28 ---- .../apm/agent/bci/InstrumentationTest.java | 23 ++-- .../ApacheHttpAsyncClientInstrumentation.java | 5 +- .../api/AbstractSpanInstrumentation.java | 12 +- .../api/ElasticApmApiInstrumentation.java | 10 +- .../plugin/api/LegacySpanInstrumentation.java | 10 +- .../api/TransactionInstrumentation.java | 4 +- ...asticsearchClientAsyncInstrumentation.java | 4 +- ...asticsearchClientAsyncInstrumentation.java | 4 +- .../concurrent/ExecutorInstrumentation.java | 10 +- .../JmsMessageConsumerInstrumentation.java | 5 +- .../kafka/KafkaProducerInstrumentation.java | 4 +- ...onsumerRecordsIteratorInstrumentation.java | 4 +- ...sumerRecordsRecordListInstrumentation.java | 4 +- ...ConsumerRecordsRecordsInstrumentation.java | 4 +- ...rrideClassLoaderLookupInstrumentation.java | 4 +- .../OkHttp3ClientAsyncInstrumentation.java | 6 +- .../okhttp/OkHttp3ClientInstrumentation.java | 4 +- .../OkHttpClientAsyncInstrumentation.java | 6 +- .../okhttp/OkHttpClientInstrumentation.java | 4 +- .../impl/ApmSpanBuilderInstrumentation.java | 4 +- .../impl/ApmSpanInstrumentation.java | 4 +- .../ExternalSpanContextInstrumentation.java | 11 +- .../impl/ScopeManagerInstrumentation.java | 6 +- .../impl/SpanContextInstrumentation.java | 8 +- .../CommonsExecAsyncInstrumentation.java | 4 +- .../agent/servlet/AsyncInstrumentation.java | 6 +- ...RequestStreamRecordingInstrumentation.java | 4 +- 33 files changed, 222 insertions(+), 319 deletions(-) delete mode 100644 apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToArgument.java delete mode 100644 apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToField.java delete mode 100644 apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToReturn.java diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignTo.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignTo.java index 4abad800fa..8fabd950cd 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignTo.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignTo.java @@ -24,22 +24,140 @@ */ package co.elastic.apm.agent.bci.bytebuddy.postprocessor; +import net.bytebuddy.dynamic.TargetType; +import net.bytebuddy.implementation.bytecode.assign.Assigner; + import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** - * Used to assign a single {@code Object[]} return value of an advice to multiple bindings. + * A limitation of non-{@linkplain net.bytebuddy.asm.Advice.OnMethodEnter#inline() inlined advices} is that the {@code readOnly} property + * of annotations that bind values to advice method parameters cannot be used. + *

+ * Because we make heavy use of non-inlined advices for + * {@linkplain co.elastic.apm.agent.bci.ElasticApmInstrumentation#indyPlugin() indy plugins}, + * this package provides alternative means to bind values: + *

+ *
    + *
  • + * {@link co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignTo.Argument}: + * Substitute of {@link net.bytebuddy.asm.Advice.Argument#readOnly()}. + *
  • + *
  • + * {@link co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignTo.Field}: + * Substitute of {@link net.bytebuddy.asm.Advice.FieldValue#readOnly()}. + *
  • + *
  • + * {@link co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignTo.Return}: + * Substitute of {@link net.bytebuddy.asm.Advice.Return#readOnly()}. + *
  • + *
  • + * {@link co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignTo}: + * Substitute of binding multiple values in a single method. + * Works by returning an {@code Object[]} from the advice method. + *
  • + *
*/ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface AssignTo { /** - * The arguments to assign to - * @return + * Overrides an argument of the instrumented method with the object at index {@link Argument#index()} + * of the {@code Object[]} returned from the advice. */ - AssignToArgument[] arguments() default {}; - AssignToField[] fields() default {}; - AssignToReturn[] returns() default {}; + Argument[] arguments() default {}; + + /** + * Overrides a field of the instrumented class with the object at index {@link Field#index()} + * of the {@code Object[]} returned from the advice. + */ + Field[] fields() default {}; + + /** + * Overrides the return value of the instrumented method with the object at index {@link Return#index()} + * of the {@code Object[]} returned from the advice. + */ + Return[] returns() default {}; + + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.METHOD) + @interface Argument { + + /** + * Returns the index of the mapped parameter. + * + * @return The index of the mapped parameter. + */ + int value(); + + /** + * The typing that should be applied when assigning the argument. + * + * @return The typing to apply upon assignment. + */ + Assigner.Typing typing() default Assigner.Typing.STATIC; + + /** + * Used in combination with {@link AssignTo} to select the index of the returned {@code Object[]} that should be used for the assignment. + * + * @return the index of the {@code Object[]} that should be used for the assignment. + */ + int index() default -1; + } + + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.METHOD) + @interface Field { + /** + * Returns the name of the field. + * + * @return The name of the field. + */ + String value(); + + /** + * Returns the type that declares the field that should be mapped to the annotated parameter. If this property + * is set to {@code void}, the field is looked up implicitly within the instrumented class's class hierarchy. + * The value can also be set to {@link TargetType} in order to look up the type on the instrumented type. + * + * @return The type that declares the field, {@code void} if this type should be determined implicitly or + * {@link TargetType} for the instrumented type. + */ + Class declaringType() default Void.class; + + /** + * The typing that should be applied when assigning the field value. + * + * @return The typing to apply upon assignment. + */ + Assigner.Typing typing() default Assigner.Typing.STATIC; + + /** + * Used in combination with {@link AssignTo} to select the index of the returned {@code Object[]} that should be used for the assignment. + * + * @return the index of the {@code Object[]} that should be used for the assignment. + */ + int index() default -1; + } + + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.METHOD) + @interface Return { + + /** + * Determines the typing that is applied when assigning the return value. + * + * @return The typing to apply when assigning the annotated parameter. + */ + Assigner.Typing typing() default Assigner.Typing.STATIC; + + /** + * Used in combination with {@link AssignTo} to select the index of the returned {@code Object[]} that should be used for the assignment. + * + * @return the index of the {@code Object[]} that should be used for the assignment. + */ + int index() default -1; + } } diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToArgument.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToArgument.java deleted file mode 100644 index d6c458f66e..0000000000 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToArgument.java +++ /dev/null @@ -1,58 +0,0 @@ -/*- - * #%L - * Elastic APM Java agent - * %% - * Copyright (C) 2018 - 2020 Elastic and contributors - * %% - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * #L% - */ -package co.elastic.apm.agent.bci.bytebuddy.postprocessor; - -import net.bytebuddy.implementation.bytecode.assign.Assigner; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.METHOD) -public @interface AssignToArgument { - - /** - * Returns the index of the mapped parameter. - * - * @return The index of the mapped parameter. - */ - int value(); - - /** - * The typing that should be applied when assigning the argument. - * - * @return The typing to apply upon assignment. - */ - Assigner.Typing typing() default Assigner.Typing.STATIC; - - /** - * Used in combination with {@link AssignTo} to select the index of the returned {@code Object[]} that should be used for the assignment. - * - * @return the index of the {@code Object[]} that should be used for the assignment. - */ - int index() default -1; -} diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToField.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToField.java deleted file mode 100644 index ab22c7fb7e..0000000000 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToField.java +++ /dev/null @@ -1,68 +0,0 @@ -/*- - * #%L - * Elastic APM Java agent - * %% - * Copyright (C) 2018 - 2020 Elastic and contributors - * %% - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * #L% - */ -package co.elastic.apm.agent.bci.bytebuddy.postprocessor; - -import net.bytebuddy.dynamic.TargetType; -import net.bytebuddy.implementation.bytecode.assign.Assigner; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.METHOD) -public @interface AssignToField { - /** - * Returns the name of the field. - * - * @return The name of the field. - */ - String value(); - - /** - * Returns the type that declares the field that should be mapped to the annotated parameter. If this property - * is set to {@code void}, the field is looked up implicitly within the instrumented class's class hierarchy. - * The value can also be set to {@link TargetType} in order to look up the type on the instrumented type. - * - * @return The type that declares the field, {@code void} if this type should be determined implicitly or - * {@link TargetType} for the instrumented type. - */ - Class declaringType() default Void.class; - - /** - * The typing that should be applied when assigning the field value. - * - * @return The typing to apply upon assignment. - */ - Assigner.Typing typing() default Assigner.Typing.STATIC; - - /** - * Used in combination with {@link AssignTo} to select the index of the returned {@code Object[]} that should be used for the assignment. - * - * @return the index of the {@code Object[]} that should be used for the assignment. - */ - int index() default -1; -} diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToPostProcessorFactory.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToPostProcessorFactory.java index c7a599696e..e59b4d4ed1 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToPostProcessorFactory.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToPostProcessorFactory.java @@ -50,7 +50,7 @@ import static net.bytebuddy.matcher.ElementMatchers.annotationType; public class AssignToPostProcessorFactory implements Advice.PostProcessor.Factory { - private static FieldLocator getFieldLocator(TypeDescription instrumentedType, AssignToField assignTo) { + private static FieldLocator getFieldLocator(TypeDescription instrumentedType, AssignTo.Field assignTo) { if (assignTo.declaringType() == Void.class) { return new FieldLocator.ForClassHierarchy(instrumentedType); } else { @@ -65,33 +65,33 @@ private static FieldLocator getFieldLocator(TypeDescription instrumentedType, As @Override public Advice.PostProcessor make(final MethodDescription.InDefinedShape adviceMethod, final boolean exit) { final AnnotationList annotations = adviceMethod.getDeclaredAnnotations() - .filter(annotationType(AssignToArgument.class) - .or(annotationType(AssignToField.class)) - .or(annotationType(AssignToReturn.class)) + .filter(annotationType(AssignTo.Argument.class) + .or(annotationType(AssignTo.Field.class)) + .or(annotationType(AssignTo.Return.class)) .or(annotationType(AssignTo.class))); if (annotations.isEmpty()) { return Advice.PostProcessor.NoOp.INSTANCE; } final List postProcessors = new ArrayList<>(); for (AnnotationDescription annotation : annotations) { - if (annotation.getAnnotationType().represents(AssignToArgument.class)) { - final AssignToArgument assignToArgument = annotations.getOnly().prepare(AssignToArgument.class).load(); + if (annotation.getAnnotationType().represents(AssignTo.Argument.class)) { + final AssignTo.Argument assignToArgument = annotations.getOnly().prepare(AssignTo.Argument.class).load(); postProcessors.add(createAssignToArgumentPostProcessor(adviceMethod, exit, assignToArgument)); - } else if (annotation.getAnnotationType().represents(AssignToField.class)) { - final AssignToField assignToField = annotations.getOnly().prepare(AssignToField.class).load(); + } else if (annotation.getAnnotationType().represents(AssignTo.Field.class)) { + final AssignTo.Field assignToField = annotations.getOnly().prepare(AssignTo.Field.class).load(); postProcessors.add(createAssignToFieldPostProcessor(adviceMethod, exit, assignToField)); - } else if (annotation.getAnnotationType().represents(AssignToReturn.class)) { - final AssignToReturn assignToReturn = annotations.getOnly().prepare(AssignToReturn.class).load(); + } else if (annotation.getAnnotationType().represents(AssignTo.Return.class)) { + final AssignTo.Return assignToReturn = annotations.getOnly().prepare(AssignTo.Return.class).load(); postProcessors.add(createAssignToReturnPostProcessor(adviceMethod, exit, assignToReturn)); } else if (annotation.getAnnotationType().represents(AssignTo.class)) { final AssignTo assignTo = annotations.getOnly().prepare(AssignTo.class).load(); - for (AssignToArgument assignToArgument : assignTo.arguments()) { + for (AssignTo.Argument assignToArgument : assignTo.arguments()) { postProcessors.add(createAssignToArgumentPostProcessor(adviceMethod, exit, assignToArgument)); } - for (AssignToField assignToField : assignTo.fields()) { + for (AssignTo.Field assignToField : assignTo.fields()) { postProcessors.add(createAssignToFieldPostProcessor(adviceMethod, exit, assignToField)); } - for (AssignToReturn assignToReturn : assignTo.returns()) { + for (AssignTo.Return assignToReturn : assignTo.returns()) { postProcessors.add(createAssignToReturnPostProcessor(adviceMethod, exit, assignToReturn)); } } @@ -109,7 +109,7 @@ public StackManipulation resolve(TypeDescription typeDescription, MethodDescript }; } - private Advice.PostProcessor createAssignToReturnPostProcessor(final MethodDescription.InDefinedShape adviceMethod, final boolean exit, final AssignToReturn assignToReturn) { + private Advice.PostProcessor createAssignToReturnPostProcessor(final MethodDescription.InDefinedShape adviceMethod, final boolean exit, final AssignTo.Return assignToReturn) { return new Advice.PostProcessor() { @Override public StackManipulation resolve(TypeDescription instrumentedType, MethodDescription instrumentedMethod, Assigner assigner, Advice.ArgumentHandler argumentHandler) { @@ -140,7 +140,7 @@ public StackManipulation resolve(TypeDescription instrumentedType, MethodDescrip }; } - private Advice.PostProcessor createAssignToFieldPostProcessor(final MethodDescription.InDefinedShape adviceMethod, final boolean exit, final AssignToField assignToField) { + private Advice.PostProcessor createAssignToFieldPostProcessor(final MethodDescription.InDefinedShape adviceMethod, final boolean exit, final AssignTo.Field assignToField) { return new Advice.PostProcessor() { @Override public StackManipulation resolve(TypeDescription instrumentedType, MethodDescription instrumentedMethod, Assigner assigner, Advice.ArgumentHandler argumentHandler) { @@ -188,7 +188,7 @@ public StackManipulation resolve(TypeDescription instrumentedType, MethodDescrip }; } - private Advice.PostProcessor createAssignToArgumentPostProcessor(final MethodDescription.InDefinedShape adviceMethod, final boolean exit, final AssignToArgument assignToArgument) { + private Advice.PostProcessor createAssignToArgumentPostProcessor(final MethodDescription.InDefinedShape adviceMethod, final boolean exit, final AssignTo.Argument assignToArgument) { return new Advice.PostProcessor() { @Override public StackManipulation resolve(TypeDescription instrumentedType, MethodDescription instrumentedMethod, Assigner assigner, Advice.ArgumentHandler argumentHandler) { diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToReturn.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToReturn.java deleted file mode 100644 index b1d9ab2626..0000000000 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignToReturn.java +++ /dev/null @@ -1,51 +0,0 @@ -/*- - * #%L - * Elastic APM Java agent - * %% - * Copyright (C) 2018 - 2020 Elastic and contributors - * %% - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * #L% - */ -package co.elastic.apm.agent.bci.bytebuddy.postprocessor; - -import net.bytebuddy.implementation.bytecode.assign.Assigner; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.METHOD) -public @interface AssignToReturn { - - /** - * Determines the typing that is applied when assigning the return value. - * - * @return The typing to apply when assigning the annotated parameter. - */ - Assigner.Typing typing() default Assigner.Typing.STATIC; - - /** - * Used in combination with {@link AssignTo} to select the index of the returned {@code Object[]} that should be used for the assignment. - * - * @return the index of the {@code Object[]} that should be used for the assignment. - */ - int index() default -1; -} diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/package-info.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/package-info.java index a8c490a24d..b39221de0a 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/package-info.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/package-info.java @@ -22,34 +22,6 @@ * under the License. * #L% */ -/** - * A limitation of non-{@linkplain net.bytebuddy.asm.Advice.OnMethodEnter#inline() inlined advices} is that the {@code readOnly} property - * of annotations that bind values to advice method parameters cannot be used. - *

- * Because we make heavy use of non-inlined advices for - * {@linkplain co.elastic.apm.agent.bci.ElasticApmInstrumentation#indyPlugin() indy plugins}, - * this package provides alternative means to bind values: - *

- *
    - *
  • - * {@link co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToArgument}: - * Substitute of {@link net.bytebuddy.asm.Advice.Argument#readOnly()}. - *
  • - *
  • - * {@link co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToField}: - * Substitute of {@link net.bytebuddy.asm.Advice.FieldValue#readOnly()}. - *
  • - *
  • - * {@link co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToReturn}: - * Substitute of {@link net.bytebuddy.asm.Advice.Return#readOnly()}. - *
  • - *
  • - * {@link co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignTo}: - * Substitute of binding multiple values in a single method. - * Works by returning an {@code Object[]} from the advice method. - *
  • - *
- */ @NonnullApi package co.elastic.apm.agent.bci.bytebuddy.postprocessor; diff --git a/apm-agent-core/src/test/java/co/elastic/apm/agent/bci/InstrumentationTest.java b/apm-agent-core/src/test/java/co/elastic/apm/agent/bci/InstrumentationTest.java index 01234ca975..65cee9ccdf 100644 --- a/apm-agent-core/src/test/java/co/elastic/apm/agent/bci/InstrumentationTest.java +++ b/apm-agent-core/src/test/java/co/elastic/apm/agent/bci/InstrumentationTest.java @@ -26,9 +26,6 @@ import co.elastic.apm.agent.MockTracer; import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignTo; -import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToArgument; -import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToField; -import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToReturn; import co.elastic.apm.agent.bci.subpackage.AdviceInSubpackageInstrumentation; import co.elastic.apm.agent.configuration.CoreConfiguration; import co.elastic.apm.agent.configuration.SpyConfiguration; @@ -404,7 +401,7 @@ private String interceptMe() { } public static class TestInstrumentation extends ElasticApmInstrumentation { - @AssignToReturn + @AssignTo.Return @Advice.OnMethodExit public static String onMethodExit() { return "intercepted"; @@ -427,7 +424,7 @@ public Collection getInstrumentationGroupNames() { } public static class MathInstrumentation extends ElasticApmInstrumentation { - @AssignToReturn + @AssignTo.Return @Advice.OnMethodExit(inline = false) public static int onMethodExit() { return 42; @@ -477,7 +474,7 @@ public static String onMethodEnter() { throw new RuntimeException("This exception should be suppressed"); } - @AssignToReturn + @AssignTo.Return @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) public static String onMethodExit(@Advice.Thrown Throwable throwable) { throw new RuntimeException("This exception should be suppressed"); @@ -501,7 +498,7 @@ public Collection getInstrumentationGroupNames() { public static class FieldAccessInstrumentation extends ElasticApmInstrumentation { - @AssignToField("privateString") + @AssignTo.Field("privateString") @Advice.OnMethodEnter public static String onEnter(@Advice.Argument(0) String s) { return s; @@ -525,7 +522,7 @@ public Collection getInstrumentationGroupNames() { public static class FieldAccessArrayInstrumentation extends ElasticApmInstrumentation { - @AssignTo(fields = @AssignToField(index = 0, value = "privateString")) + @AssignTo(fields = @AssignTo.Field(index = 0, value = "privateString")) @Advice.OnMethodEnter public static Object[] onEnter(@Advice.Argument(0) String s) { return new Object[]{s}; @@ -549,7 +546,7 @@ public Collection getInstrumentationGroupNames() { public static class AssignToArgumentInstrumentation extends ElasticApmInstrumentation { - @AssignToArgument(0) + @AssignTo.Argument(0) @Advice.OnMethodEnter public static String onEnter(@Advice.Argument(0) String s) { return s + "@AssignToArgument"; @@ -574,8 +571,8 @@ public Collection getInstrumentationGroupNames() { public static class AssignToArgumentsInstrumentation extends ElasticApmInstrumentation { @AssignTo(arguments = { - @AssignToArgument(index = 0, value = 1), - @AssignToArgument(index = 1, value = 0) + @AssignTo.Argument(index = 0, value = 1), + @AssignTo.Argument(index = 1, value = 0) }) @Advice.OnMethodEnter(inline = false) public static Object[] onEnter(@Advice.Argument(0) String foo, @Advice.Argument(1) String bar) { @@ -600,7 +597,7 @@ public Collection getInstrumentationGroupNames() { public static class AssignToReturnArrayInstrumentation extends ElasticApmInstrumentation { - @AssignTo(returns = @AssignToReturn(index = 0)) + @AssignTo(returns = @AssignTo.Return(index = 0)) @Advice.OnMethodExit(inline = false) public static Object[] onEnter(@Advice.Argument(0) String foo, @Advice.Argument(1) String bar) { return new Object[]{foo + bar}; @@ -768,7 +765,7 @@ public boolean indyPlugin() { public static class ClassLoadingTestInstrumentation extends ElasticApmInstrumentation { - @AssignToReturn + @AssignTo.Return @Advice.OnMethodExit(inline = false) public static ClassLoader onExit() { return ClassLoadingTestInstrumentation.class.getClassLoader(); diff --git a/apm-agent-plugins/apm-apache-httpclient-plugin/src/main/java/co/elastic/apm/agent/httpclient/ApacheHttpAsyncClientInstrumentation.java b/apm-agent-plugins/apm-apache-httpclient-plugin/src/main/java/co/elastic/apm/agent/httpclient/ApacheHttpAsyncClientInstrumentation.java index 38340d791a..abb4a2ab1f 100644 --- a/apm-agent-plugins/apm-apache-httpclient-plugin/src/main/java/co/elastic/apm/agent/httpclient/ApacheHttpAsyncClientInstrumentation.java +++ b/apm-agent-plugins/apm-apache-httpclient-plugin/src/main/java/co/elastic/apm/agent/httpclient/ApacheHttpAsyncClientInstrumentation.java @@ -27,7 +27,6 @@ import co.elastic.apm.agent.bci.HelperClassManager; import co.elastic.apm.agent.bci.VisibleForAdvice; import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignTo; -import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToArgument; import co.elastic.apm.agent.http.client.HttpClientHelper; import co.elastic.apm.agent.httpclient.helper.ApacheHttpAsyncClientHelper; import co.elastic.apm.agent.impl.ElasticApmTracer; @@ -104,8 +103,8 @@ public ElementMatcher getMethodMatcher() { public static class ApacheHttpAsyncClientAdvice { @AssignTo(arguments = { - @AssignToArgument(index = 0, value = 0), - @AssignToArgument(index = 1, value = 3) + @AssignTo.Argument(index = 0, value = 0), + @AssignTo.Argument(index = 1, value = 3) }) @Nullable @Advice.OnMethodEnter(suppress = Throwable.class) diff --git a/apm-agent-plugins/apm-api-plugin/src/main/java/co/elastic/apm/agent/plugin/api/AbstractSpanInstrumentation.java b/apm-agent-plugins/apm-api-plugin/src/main/java/co/elastic/apm/agent/plugin/api/AbstractSpanInstrumentation.java index fd54ca696a..0a8c4c543e 100644 --- a/apm-agent-plugins/apm-api-plugin/src/main/java/co/elastic/apm/agent/plugin/api/AbstractSpanInstrumentation.java +++ b/apm-agent-plugins/apm-api-plugin/src/main/java/co/elastic/apm/agent/plugin/api/AbstractSpanInstrumentation.java @@ -25,7 +25,7 @@ package co.elastic.apm.agent.plugin.api; import co.elastic.apm.agent.bci.VisibleForAdvice; -import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToReturn; +import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignTo; import co.elastic.apm.agent.impl.transaction.AbstractSpan; import co.elastic.apm.agent.impl.transaction.Span; import co.elastic.apm.agent.impl.transaction.Transaction; @@ -117,7 +117,7 @@ public DoCreateSpanInstrumentation() { } @VisibleForAdvice - @AssignToReturn + @AssignTo.Return @Advice.OnMethodExit(suppress = Throwable.class) public static Span doCreateSpan(@Advice.FieldValue(value = "span", typing = Assigner.Typing.DYNAMIC) AbstractSpan context) { return context.createSpan(); @@ -173,7 +173,7 @@ public CaptureExceptionInstrumentation() { } @Nullable - @AssignToReturn + @AssignTo.Return @VisibleForAdvice @Advice.OnMethodExit(suppress = Throwable.class) public static String captureException(@Advice.FieldValue(value = "span", typing = Assigner.Typing.DYNAMIC) AbstractSpan context, @@ -206,7 +206,7 @@ public GetIdInstrumentation() { super(named("getId").and(takesArguments(0))); } - @AssignToReturn + @AssignTo.Return @VisibleForAdvice @Advice.OnMethodExit(suppress = Throwable.class) public static String getId(@Advice.FieldValue(value = "span", typing = Assigner.Typing.DYNAMIC) AbstractSpan context) { @@ -219,7 +219,7 @@ public GetTraceIdInstrumentation() { super(named("getTraceId").and(takesArguments(0))); } - @AssignToReturn + @AssignTo.Return @VisibleForAdvice @Advice.OnMethodExit(suppress = Throwable.class) public static String getTraceId(@Advice.FieldValue(value = "span", typing = Assigner.Typing.DYNAMIC) AbstractSpan context) { @@ -289,7 +289,7 @@ public IsSampledInstrumentation() { super(named("isSampled")); } - @AssignToReturn + @AssignTo.Return @VisibleForAdvice @Advice.OnMethodExit(suppress = Throwable.class) public static boolean isSampled(@Advice.FieldValue(value = "span", typing = Assigner.Typing.DYNAMIC) AbstractSpan context) { diff --git a/apm-agent-plugins/apm-api-plugin/src/main/java/co/elastic/apm/agent/plugin/api/ElasticApmApiInstrumentation.java b/apm-agent-plugins/apm-api-plugin/src/main/java/co/elastic/apm/agent/plugin/api/ElasticApmApiInstrumentation.java index 58b4022b42..c41b5eb0ac 100644 --- a/apm-agent-plugins/apm-api-plugin/src/main/java/co/elastic/apm/agent/plugin/api/ElasticApmApiInstrumentation.java +++ b/apm-agent-plugins/apm-api-plugin/src/main/java/co/elastic/apm/agent/plugin/api/ElasticApmApiInstrumentation.java @@ -25,7 +25,7 @@ package co.elastic.apm.agent.plugin.api; import co.elastic.apm.agent.bci.VisibleForAdvice; -import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToReturn; +import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignTo; import co.elastic.apm.agent.impl.transaction.Transaction; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.method.MethodDescription; @@ -66,7 +66,7 @@ public StartTransactionInstrumentation() { } @Nullable - @AssignToReturn + @AssignTo.Return @VisibleForAdvice @Advice.OnMethodExit(suppress = Throwable.class) public static Object doStartTransaction(@Advice.Origin Class clazz) { @@ -88,7 +88,7 @@ public StartTransactionWithRemoteParentInstrumentation() { } @Nullable - @AssignToReturn + @AssignTo.Return @SuppressWarnings({"UnusedAssignment", "ParameterCanBeLocal", "unused"}) @VisibleForAdvice @Advice.OnMethodExit(suppress = Throwable.class) @@ -123,7 +123,7 @@ public CurrentTransactionInstrumentation() { } @Nullable - @AssignToReturn + @AssignTo.Return @VisibleForAdvice @Advice.OnMethodExit(suppress = Throwable.class) public static Object doGetCurrentTransaction() { @@ -140,7 +140,7 @@ public CurrentSpanInstrumentation() { } @Nullable - @AssignToReturn + @AssignTo.Return @VisibleForAdvice @Advice.OnMethodExit(suppress = Throwable.class) public static Object doGetCurrentSpan() { diff --git a/apm-agent-plugins/apm-api-plugin/src/main/java/co/elastic/apm/agent/plugin/api/LegacySpanInstrumentation.java b/apm-agent-plugins/apm-api-plugin/src/main/java/co/elastic/apm/agent/plugin/api/LegacySpanInstrumentation.java index f8d98bcc5b..64e93ce19c 100644 --- a/apm-agent-plugins/apm-api-plugin/src/main/java/co/elastic/apm/agent/plugin/api/LegacySpanInstrumentation.java +++ b/apm-agent-plugins/apm-api-plugin/src/main/java/co/elastic/apm/agent/plugin/api/LegacySpanInstrumentation.java @@ -25,7 +25,7 @@ package co.elastic.apm.agent.plugin.api; import co.elastic.apm.agent.bci.VisibleForAdvice; -import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToReturn; +import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignTo; import co.elastic.apm.agent.impl.transaction.AbstractSpan; import co.elastic.apm.agent.impl.transaction.Span; import net.bytebuddy.asm.Advice; @@ -102,7 +102,7 @@ public DoCreateSpanInstrumentation() { super(named("doCreateSpan")); } - @AssignToReturn + @AssignTo.Return @VisibleForAdvice @Advice.OnMethodExit(inline = false) public static Span doCreateSpan(@Advice.FieldValue(value = "span", typing = Assigner.Typing.DYNAMIC) AbstractSpan span) { @@ -142,7 +142,7 @@ public GetIdInstrumentation() { } @Nullable - @AssignToReturn + @AssignTo.Return @VisibleForAdvice @Advice.OnMethodExit(inline = false) public static String getId(@Advice.FieldValue(value = "span", typing = Assigner.Typing.DYNAMIC) AbstractSpan span) { @@ -159,7 +159,7 @@ public GetTraceIdInstrumentation() { } @Nullable - @AssignToReturn + @AssignTo.Return @VisibleForAdvice @Advice.OnMethodExit(inline = false) public static String getTraceId(@Advice.FieldValue(value = "span", typing = Assigner.Typing.DYNAMIC) AbstractSpan span) { @@ -200,7 +200,7 @@ public IsSampledInstrumentation() { super(named("isSampled")); } - @AssignToReturn + @AssignTo.Return @VisibleForAdvice @Advice.OnMethodExit(inline = false) public static boolean addTag(@Advice.FieldValue(value = "span", typing = Assigner.Typing.DYNAMIC) AbstractSpan span) { diff --git a/apm-agent-plugins/apm-api-plugin/src/main/java/co/elastic/apm/agent/plugin/api/TransactionInstrumentation.java b/apm-agent-plugins/apm-api-plugin/src/main/java/co/elastic/apm/agent/plugin/api/TransactionInstrumentation.java index 31972fd995..57e20b1639 100644 --- a/apm-agent-plugins/apm-api-plugin/src/main/java/co/elastic/apm/agent/plugin/api/TransactionInstrumentation.java +++ b/apm-agent-plugins/apm-api-plugin/src/main/java/co/elastic/apm/agent/plugin/api/TransactionInstrumentation.java @@ -25,7 +25,7 @@ package co.elastic.apm.agent.plugin.api; import co.elastic.apm.agent.bci.VisibleForAdvice; -import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToReturn; +import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignTo; import co.elastic.apm.agent.impl.transaction.TraceContext; import co.elastic.apm.agent.impl.transaction.Transaction; import net.bytebuddy.asm.Advice; @@ -78,7 +78,7 @@ public EnsureParentIdInstrumentation() { } @Nullable - @AssignToReturn + @AssignTo.Return @VisibleForAdvice @Advice.OnMethodExit(suppress = Throwable.class) public static String ensureParentId(@Advice.FieldValue(value = "span", typing = Assigner.Typing.DYNAMIC) Transaction transaction) { diff --git a/apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-5_6/src/main/java/co/elastic/apm/agent/es/restclient/v5_6/ElasticsearchClientAsyncInstrumentation.java b/apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-5_6/src/main/java/co/elastic/apm/agent/es/restclient/v5_6/ElasticsearchClientAsyncInstrumentation.java index 5e53ecda30..44c9cdb1be 100644 --- a/apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-5_6/src/main/java/co/elastic/apm/agent/es/restclient/v5_6/ElasticsearchClientAsyncInstrumentation.java +++ b/apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-5_6/src/main/java/co/elastic/apm/agent/es/restclient/v5_6/ElasticsearchClientAsyncInstrumentation.java @@ -25,7 +25,7 @@ package co.elastic.apm.agent.es.restclient.v5_6; import co.elastic.apm.agent.bci.VisibleForAdvice; -import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToArgument; +import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignTo; import co.elastic.apm.agent.es.restclient.ElasticsearchRestClientInstrumentation; import co.elastic.apm.agent.es.restclient.ElasticsearchRestClientInstrumentationHelper; import co.elastic.apm.agent.impl.ElasticApmTracer; @@ -79,7 +79,7 @@ public ElementMatcher getMethodMatcher() { public static class ElasticsearchRestClientAsyncAdvice { @VisibleForAdvice public static final RemoveOnGetThreadLocal spanTls = RemoveOnGetThreadLocal.get(ElasticsearchRestClientAsyncAdvice.class, "spanTls"); - @AssignToArgument(5) + @AssignTo.Argument(5) @Advice.OnMethodEnter(suppress = Throwable.class) public static ResponseListener onBeforeExecute(@Advice.Argument(0) String method, @Advice.Argument(1) String endpoint, diff --git a/apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-6_4/src/main/java/co/elastic/apm/agent/es/restclient/v6_4/ElasticsearchClientAsyncInstrumentation.java b/apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-6_4/src/main/java/co/elastic/apm/agent/es/restclient/v6_4/ElasticsearchClientAsyncInstrumentation.java index 8b65462eb8..eae0251fec 100644 --- a/apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-6_4/src/main/java/co/elastic/apm/agent/es/restclient/v6_4/ElasticsearchClientAsyncInstrumentation.java +++ b/apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-6_4/src/main/java/co/elastic/apm/agent/es/restclient/v6_4/ElasticsearchClientAsyncInstrumentation.java @@ -25,7 +25,7 @@ package co.elastic.apm.agent.es.restclient.v6_4; import co.elastic.apm.agent.bci.VisibleForAdvice; -import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToArgument; +import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignTo; import co.elastic.apm.agent.es.restclient.ElasticsearchRestClientInstrumentation; import co.elastic.apm.agent.es.restclient.ElasticsearchRestClientInstrumentationHelper; import co.elastic.apm.agent.impl.ElasticApmTracer; @@ -74,7 +74,7 @@ public static class ElasticsearchRestClientAsyncAdvice { @VisibleForAdvice public static final RemoveOnGetThreadLocal spanTls = RemoveOnGetThreadLocal.get(ElasticsearchRestClientAsyncAdvice.class, "spanTls"); - @AssignToArgument(1) + @AssignTo.Argument(1) @Advice.OnMethodEnter(suppress = Throwable.class) public static ResponseListener onBeforeExecute(@Advice.Argument(0) Request request, @Advice.Argument(1) ResponseListener responseListener) { diff --git a/apm-agent-plugins/apm-java-concurrent-plugin/src/main/java/co/elastic/apm/agent/concurrent/ExecutorInstrumentation.java b/apm-agent-plugins/apm-java-concurrent-plugin/src/main/java/co/elastic/apm/agent/concurrent/ExecutorInstrumentation.java index 459a37374f..ac33bff9f3 100644 --- a/apm-agent-plugins/apm-java-concurrent-plugin/src/main/java/co/elastic/apm/agent/concurrent/ExecutorInstrumentation.java +++ b/apm-agent-plugins/apm-java-concurrent-plugin/src/main/java/co/elastic/apm/agent/concurrent/ExecutorInstrumentation.java @@ -25,7 +25,7 @@ package co.elastic.apm.agent.concurrent; import co.elastic.apm.agent.bci.ElasticApmInstrumentation; -import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToArgument; +import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignTo; import co.elastic.apm.agent.util.GlobalVariables; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.NamedElement; @@ -111,7 +111,7 @@ private static boolean isExcluded(@Advice.This Executor executor) { public static class ExecutorRunnableInstrumentation extends ExecutorInstrumentation { @Nullable - @AssignToArgument(0) + @AssignTo.Argument(0) @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) public static Runnable onExecute(@Advice.This Executor thiz, @Advice.Argument(0) @Nullable Runnable runnable) { @@ -147,7 +147,7 @@ public ElementMatcher getMethodMatcher() { public static class ExecutorCallableInstrumentation extends ExecutorInstrumentation { @Nullable - @AssignToArgument(0) + @AssignTo.Argument(0) @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) public static Callable onSubmit(@Advice.This Executor thiz, @Advice.Argument(0) @Nullable Callable callable) { @@ -195,7 +195,7 @@ public ElementMatcher getMethodMatcher() { } @Nullable - @AssignToArgument(0) + @AssignTo.Argument(0) @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) public static Collection> onEnter(@Advice.This Executor thiz, @Nullable @Advice.Argument(0) Collection> callables) { @@ -239,7 +239,7 @@ public ElementMatcher getMethodMatcher() { } @Nullable - @AssignToArgument(0) + @AssignTo.Argument(0) @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) public static ForkJoinTask onExecute(@Advice.This Executor thiz, @Advice.Argument(0) @Nullable ForkJoinTask task) { diff --git a/apm-agent-plugins/apm-jms-plugin/src/main/java/co/elastic/apm/agent/jms/JmsMessageConsumerInstrumentation.java b/apm-agent-plugins/apm-jms-plugin/src/main/java/co/elastic/apm/agent/jms/JmsMessageConsumerInstrumentation.java index 1be142a74e..e73d8259cc 100644 --- a/apm-agent-plugins/apm-jms-plugin/src/main/java/co/elastic/apm/agent/jms/JmsMessageConsumerInstrumentation.java +++ b/apm-agent-plugins/apm-jms-plugin/src/main/java/co/elastic/apm/agent/jms/JmsMessageConsumerInstrumentation.java @@ -25,8 +25,7 @@ package co.elastic.apm.agent.jms; import co.elastic.apm.agent.bci.VisibleForAdvice; -import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToArgument; -import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToReturn; +import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignTo; import co.elastic.apm.agent.configuration.MessagingConfiguration; import co.elastic.apm.agent.impl.ElasticApmTracer; import co.elastic.apm.agent.impl.transaction.AbstractSpan; @@ -264,7 +263,7 @@ public Class getAdviceClass() { public static class ListenerWrappingAdvice { @Nullable - @AssignToArgument(0) + @AssignTo.Argument(0) @Advice.OnMethodEnter(inline = false) public static MessageListener beforeSetListener(@Advice.Argument(0) @Nullable MessageListener original) { //noinspection ConstantConditions - the Advice must be invoked only if the BaseJmsInstrumentation constructor was invoked diff --git a/apm-agent-plugins/apm-kafka-plugin/apm-kafka-base-plugin/src/main/java/co/elastic/apm/agent/kafka/KafkaProducerInstrumentation.java b/apm-agent-plugins/apm-kafka-plugin/apm-kafka-base-plugin/src/main/java/co/elastic/apm/agent/kafka/KafkaProducerInstrumentation.java index 51d854f5de..0c6173edc5 100644 --- a/apm-agent-plugins/apm-kafka-plugin/apm-kafka-base-plugin/src/main/java/co/elastic/apm/agent/kafka/KafkaProducerInstrumentation.java +++ b/apm-agent-plugins/apm-kafka-plugin/apm-kafka-base-plugin/src/main/java/co/elastic/apm/agent/kafka/KafkaProducerInstrumentation.java @@ -24,7 +24,7 @@ */ package co.elastic.apm.agent.kafka; -import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToArgument; +import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignTo; import co.elastic.apm.agent.impl.ElasticApmTracer; import co.elastic.apm.agent.impl.transaction.Span; import co.elastic.apm.agent.kafka.helper.KafkaInstrumentationHelper; @@ -75,7 +75,7 @@ public Class getAdviceClass() { @SuppressWarnings("rawtypes") public static class KafkaProducerAdvice { @Nullable - @AssignToArgument(1) + @AssignTo.Argument(1) @Advice.OnMethodEnter(suppress = Throwable.class) public static Callback beforeSend(@Advice.Argument(0) final ProducerRecord record, @Advice.Argument(1) @Nullable Callback callback) { diff --git a/apm-agent-plugins/apm-kafka-plugin/apm-kafka-headers-plugin/src/main/java/co/elastic/apm/agent/kafka/ConsumerRecordsIteratorInstrumentation.java b/apm-agent-plugins/apm-kafka-plugin/apm-kafka-headers-plugin/src/main/java/co/elastic/apm/agent/kafka/ConsumerRecordsIteratorInstrumentation.java index a9fc39d4c3..aa1e108d30 100644 --- a/apm-agent-plugins/apm-kafka-plugin/apm-kafka-headers-plugin/src/main/java/co/elastic/apm/agent/kafka/ConsumerRecordsIteratorInstrumentation.java +++ b/apm-agent-plugins/apm-kafka-plugin/apm-kafka-headers-plugin/src/main/java/co/elastic/apm/agent/kafka/ConsumerRecordsIteratorInstrumentation.java @@ -24,7 +24,7 @@ */ package co.elastic.apm.agent.kafka; -import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToReturn; +import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignTo; import co.elastic.apm.agent.impl.ElasticApmTracer; import co.elastic.apm.agent.kafka.helper.KafkaInstrumentationHeadersHelper; import net.bytebuddy.asm.Advice; @@ -68,7 +68,7 @@ public Class getAdviceClass() { public static class ConsumerRecordsAdvice { @Nullable - @AssignToReturn + @AssignTo.Return @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) public static Iterator wrapIterator(@Nullable @Advice.Return Iterator iterator) { if (tracer == null || !tracer.isRunning() || tracer.currentTransaction() != null) { diff --git a/apm-agent-plugins/apm-kafka-plugin/apm-kafka-headers-plugin/src/main/java/co/elastic/apm/agent/kafka/ConsumerRecordsRecordListInstrumentation.java b/apm-agent-plugins/apm-kafka-plugin/apm-kafka-headers-plugin/src/main/java/co/elastic/apm/agent/kafka/ConsumerRecordsRecordListInstrumentation.java index 6ab5f9d312..5a75aac356 100644 --- a/apm-agent-plugins/apm-kafka-plugin/apm-kafka-headers-plugin/src/main/java/co/elastic/apm/agent/kafka/ConsumerRecordsRecordListInstrumentation.java +++ b/apm-agent-plugins/apm-kafka-plugin/apm-kafka-headers-plugin/src/main/java/co/elastic/apm/agent/kafka/ConsumerRecordsRecordListInstrumentation.java @@ -24,7 +24,7 @@ */ package co.elastic.apm.agent.kafka; -import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToReturn; +import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignTo; import co.elastic.apm.agent.impl.ElasticApmTracer; import co.elastic.apm.agent.kafka.helper.KafkaInstrumentationHeadersHelper; import net.bytebuddy.asm.Advice; @@ -69,7 +69,7 @@ public Class getAdviceClass() { public static class ConsumerRecordsAdvice { @Nullable - @AssignToReturn + @AssignTo.Return @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) public static List wrapRecordList(@Nullable @Advice.Return List list) { if (tracer == null || !tracer.isRunning() || tracer.currentTransaction() != null) { diff --git a/apm-agent-plugins/apm-kafka-plugin/apm-kafka-headers-plugin/src/main/java/co/elastic/apm/agent/kafka/ConsumerRecordsRecordsInstrumentation.java b/apm-agent-plugins/apm-kafka-plugin/apm-kafka-headers-plugin/src/main/java/co/elastic/apm/agent/kafka/ConsumerRecordsRecordsInstrumentation.java index f0412b7549..43bdbf9dd9 100644 --- a/apm-agent-plugins/apm-kafka-plugin/apm-kafka-headers-plugin/src/main/java/co/elastic/apm/agent/kafka/ConsumerRecordsRecordsInstrumentation.java +++ b/apm-agent-plugins/apm-kafka-plugin/apm-kafka-headers-plugin/src/main/java/co/elastic/apm/agent/kafka/ConsumerRecordsRecordsInstrumentation.java @@ -24,7 +24,7 @@ */ package co.elastic.apm.agent.kafka; -import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToReturn; +import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignTo; import co.elastic.apm.agent.impl.ElasticApmTracer; import co.elastic.apm.agent.kafka.helper.KafkaInstrumentationHeadersHelper; import net.bytebuddy.asm.Advice; @@ -67,7 +67,7 @@ public Class getAdviceClass() { public static class ConsumerRecordsAdvice { @Nullable - @AssignToReturn + @AssignTo.Return @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) public static Iterable wrapIterable(@Nullable @Advice.Return Iterable iterable) { if (tracer == null || !tracer.isRunning() || tracer.currentTransaction() != null) { diff --git a/apm-agent-plugins/apm-mule4-plugin/src/main/java/co/elastic/apm/agent/mule4/Mule4OverrideClassLoaderLookupInstrumentation.java b/apm-agent-plugins/apm-mule4-plugin/src/main/java/co/elastic/apm/agent/mule4/Mule4OverrideClassLoaderLookupInstrumentation.java index 7f3ec8dc8f..92a81cc141 100644 --- a/apm-agent-plugins/apm-mule4-plugin/src/main/java/co/elastic/apm/agent/mule4/Mule4OverrideClassLoaderLookupInstrumentation.java +++ b/apm-agent-plugins/apm-mule4-plugin/src/main/java/co/elastic/apm/agent/mule4/Mule4OverrideClassLoaderLookupInstrumentation.java @@ -25,7 +25,7 @@ package co.elastic.apm.agent.mule4; import co.elastic.apm.agent.bci.ElasticApmInstrumentation; -import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToReturn; +import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignTo; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.NamedElement; import net.bytebuddy.description.method.MethodDescription; @@ -78,7 +78,7 @@ public Class getAdviceClass() { } public static class Mule4OverrideClassLoaderLookupAdvice { - @AssignToReturn + @AssignTo.Return @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) public static LookupStrategy makeParentOnlyForAgentClasses(@Advice.Argument(0) @Nullable final String packageName, @Advice.Return LookupStrategy lookupStrategy) { diff --git a/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttp3ClientAsyncInstrumentation.java b/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttp3ClientAsyncInstrumentation.java index 432cbec5be..fec1731d49 100644 --- a/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttp3ClientAsyncInstrumentation.java +++ b/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttp3ClientAsyncInstrumentation.java @@ -27,8 +27,6 @@ import co.elastic.apm.agent.bci.HelperClassManager; import co.elastic.apm.agent.bci.VisibleForAdvice; import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignTo; -import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToArgument; -import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToField; import co.elastic.apm.agent.http.client.HttpClientHelper; import co.elastic.apm.agent.impl.ElasticApmTracer; import co.elastic.apm.agent.impl.transaction.AbstractSpan; @@ -83,8 +81,8 @@ public OkHttp3ClientAsyncInstrumentation(ElasticApmTracer tracer) { public static class OkHttpClient3ExecuteAdvice { @AssignTo( - fields = @AssignToField(index = 0, value = "originalRequest"), - arguments = @AssignToArgument(index = 1, value = 0) + fields = @AssignTo.Field(index = 0, value = "originalRequest"), + arguments = @AssignTo.Argument(index = 1, value = 0) ) @Nullable @Advice.OnMethodEnter(suppress = Throwable.class) diff --git a/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttp3ClientInstrumentation.java b/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttp3ClientInstrumentation.java index 340e8f3c58..05dba2f337 100644 --- a/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttp3ClientInstrumentation.java +++ b/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttp3ClientInstrumentation.java @@ -25,7 +25,7 @@ package co.elastic.apm.agent.okhttp; import co.elastic.apm.agent.bci.VisibleForAdvice; -import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToField; +import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignTo; import co.elastic.apm.agent.http.client.HttpClientHelper; import co.elastic.apm.agent.impl.ElasticApmTracer; import co.elastic.apm.agent.impl.transaction.AbstractSpan; @@ -63,7 +63,7 @@ public static class OkHttpClient3ExecuteAdvice { public final static RemoveOnGetThreadLocal spanTls = RemoveOnGetThreadLocal.get(OkHttpClient3ExecuteAdvice.class, "spanTls"); @Nullable - @AssignToField(value = "originalRequest", typing = Assigner.Typing.DYNAMIC) + @AssignTo.Field(value = "originalRequest", typing = Assigner.Typing.DYNAMIC) @Advice.OnMethodEnter(suppress = Throwable.class) public static Object onBeforeExecute(final @Advice.FieldValue("originalRequest") @Nullable Object originalRequest) { diff --git a/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttpClientAsyncInstrumentation.java b/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttpClientAsyncInstrumentation.java index 9c88e26e1d..6250bb2d49 100644 --- a/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttpClientAsyncInstrumentation.java +++ b/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttpClientAsyncInstrumentation.java @@ -27,8 +27,6 @@ import co.elastic.apm.agent.bci.HelperClassManager; import co.elastic.apm.agent.bci.VisibleForAdvice; import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignTo; -import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToArgument; -import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToField; import co.elastic.apm.agent.http.client.HttpClientHelper; import co.elastic.apm.agent.impl.ElasticApmTracer; import co.elastic.apm.agent.impl.transaction.AbstractSpan; @@ -81,8 +79,8 @@ public OkHttpClientAsyncInstrumentation(ElasticApmTracer tracer) { public static class OkHttpClient3ExecuteAdvice { @AssignTo( - fields = @AssignToField(index = 0, value = "originalRequest"), - arguments = @AssignToArgument(index = 1, value = 0) + fields = @AssignTo.Field(index = 0, value = "originalRequest"), + arguments = @AssignTo.Argument(index = 1, value = 0) ) @Nullable @Advice.OnMethodEnter(suppress = Throwable.class) diff --git a/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttpClientInstrumentation.java b/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttpClientInstrumentation.java index d958c25ab9..b931e5576a 100644 --- a/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttpClientInstrumentation.java +++ b/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttpClientInstrumentation.java @@ -25,7 +25,7 @@ package co.elastic.apm.agent.okhttp; import co.elastic.apm.agent.bci.VisibleForAdvice; -import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToField; +import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignTo; import co.elastic.apm.agent.http.client.HttpClientHelper; import co.elastic.apm.agent.impl.ElasticApmTracer; import co.elastic.apm.agent.impl.transaction.AbstractSpan; @@ -62,7 +62,7 @@ public static class OkHttpClientExecuteAdvice { public final static RemoveOnGetThreadLocal spanTls = RemoveOnGetThreadLocal.get(OkHttpClientExecuteAdvice.class, "spanTls"); @Nullable - @AssignToField(value = "originalRequest", typing = Assigner.Typing.DYNAMIC) + @AssignTo.Field(value = "originalRequest", typing = Assigner.Typing.DYNAMIC) @Advice.OnMethodEnter(suppress = Throwable.class) public static Object onBeforeExecute(@Advice.FieldValue("originalRequest") @Nullable Object originalRequest) { diff --git a/apm-agent-plugins/apm-opentracing-plugin/src/main/java/co/elastic/apm/agent/opentracing/impl/ApmSpanBuilderInstrumentation.java b/apm-agent-plugins/apm-opentracing-plugin/src/main/java/co/elastic/apm/agent/opentracing/impl/ApmSpanBuilderInstrumentation.java index f05b638b85..568d76f399 100644 --- a/apm-agent-plugins/apm-opentracing-plugin/src/main/java/co/elastic/apm/agent/opentracing/impl/ApmSpanBuilderInstrumentation.java +++ b/apm-agent-plugins/apm-opentracing-plugin/src/main/java/co/elastic/apm/agent/opentracing/impl/ApmSpanBuilderInstrumentation.java @@ -25,7 +25,7 @@ package co.elastic.apm.agent.opentracing.impl; import co.elastic.apm.agent.bci.VisibleForAdvice; -import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToReturn; +import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignTo; import co.elastic.apm.agent.impl.ElasticApmTracer; import co.elastic.apm.agent.impl.sampling.ConstantSampler; import co.elastic.apm.agent.impl.sampling.Sampler; @@ -74,7 +74,7 @@ public CreateSpanInstrumentation() { } @Nullable - @AssignToReturn + @AssignTo.Return @Advice.OnMethodExit(suppress = Throwable.class) public static Object createSpan(@Advice.Argument(value = 0, typing = Assigner.Typing.DYNAMIC) @Nullable AbstractSpan parentContext, @Advice.Origin Class spanBuilderClass, diff --git a/apm-agent-plugins/apm-opentracing-plugin/src/main/java/co/elastic/apm/agent/opentracing/impl/ApmSpanInstrumentation.java b/apm-agent-plugins/apm-opentracing-plugin/src/main/java/co/elastic/apm/agent/opentracing/impl/ApmSpanInstrumentation.java index 0be3a33f70..6c36d78e59 100644 --- a/apm-agent-plugins/apm-opentracing-plugin/src/main/java/co/elastic/apm/agent/opentracing/impl/ApmSpanInstrumentation.java +++ b/apm-agent-plugins/apm-opentracing-plugin/src/main/java/co/elastic/apm/agent/opentracing/impl/ApmSpanInstrumentation.java @@ -25,7 +25,7 @@ package co.elastic.apm.agent.opentracing.impl; import co.elastic.apm.agent.bci.VisibleForAdvice; -import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToReturn; +import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignTo; import co.elastic.apm.agent.impl.context.web.ResultUtil; import co.elastic.apm.agent.impl.transaction.AbstractSpan; import co.elastic.apm.agent.impl.transaction.Span; @@ -292,7 +292,7 @@ public GetTraceContextInstrumentation() { } @Nullable - @AssignToReturn + @AssignTo.Return @Advice.OnMethodExit(suppress = Throwable.class) public static Object getTraceContext(@Advice.Argument(value = 0, typing = Assigner.Typing.DYNAMIC) @Nullable AbstractSpan abstractSpan) { return abstractSpan; diff --git a/apm-agent-plugins/apm-opentracing-plugin/src/main/java/co/elastic/apm/agent/opentracing/impl/ExternalSpanContextInstrumentation.java b/apm-agent-plugins/apm-opentracing-plugin/src/main/java/co/elastic/apm/agent/opentracing/impl/ExternalSpanContextInstrumentation.java index 221e05e4e1..a54a1cad9d 100644 --- a/apm-agent-plugins/apm-opentracing-plugin/src/main/java/co/elastic/apm/agent/opentracing/impl/ExternalSpanContextInstrumentation.java +++ b/apm-agent-plugins/apm-opentracing-plugin/src/main/java/co/elastic/apm/agent/opentracing/impl/ExternalSpanContextInstrumentation.java @@ -25,8 +25,7 @@ package co.elastic.apm.agent.opentracing.impl; import co.elastic.apm.agent.bci.VisibleForAdvice; -import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToField; -import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToReturn; +import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignTo; import co.elastic.apm.agent.impl.transaction.TraceContext; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.method.MethodDescription; @@ -68,7 +67,7 @@ public ToTraceIdInstrumentation() { } @Nullable - @AssignToField(value = "childTraceContext") + @AssignTo.Field(value = "childTraceContext") @Advice.OnMethodEnter(suppress = Throwable.class) public static TraceContext toTraceId(@Advice.FieldValue(value = "textMap", typing = Assigner.Typing.DYNAMIC) @Nullable Iterable> textMap, @Advice.FieldValue(value = "childTraceContext", typing = Assigner.Typing.DYNAMIC) @Nullable TraceContext childTraceContext) { @@ -81,7 +80,7 @@ public static TraceContext toTraceId(@Advice.FieldValue(value = "textMap", typin @Nullable - @AssignToReturn + @AssignTo.Return @Advice.OnMethodExit(suppress = Throwable.class) public static String onExit(@Advice.FieldValue(value = "childTraceContext", typing = Assigner.Typing.DYNAMIC) @Nullable TraceContext childTraceContext) { if (childTraceContext == null) { @@ -98,7 +97,7 @@ public ToSpanIdInstrumentation() { } @Nullable - @AssignToField(value = "childTraceContext") + @AssignTo.Field(value = "childTraceContext") @Advice.OnMethodEnter(suppress = Throwable.class) public static TraceContext toSpanId(@Advice.FieldValue(value = "textMap", typing = Assigner.Typing.DYNAMIC) @Nullable Iterable> textMap, @Advice.FieldValue(value = "childTraceContext", typing = Assigner.Typing.DYNAMIC) @Nullable TraceContext childTraceContext) { @@ -109,7 +108,7 @@ public static TraceContext toSpanId(@Advice.FieldValue(value = "textMap", typing } @Nullable - @AssignToReturn + @AssignTo.Return @Advice.OnMethodExit(suppress = Throwable.class) public static String onExit(@Advice.FieldValue(value = "childTraceContext", typing = Assigner.Typing.DYNAMIC) @Nullable TraceContext childTraceContext) { if (childTraceContext == null) { diff --git a/apm-agent-plugins/apm-opentracing-plugin/src/main/java/co/elastic/apm/agent/opentracing/impl/ScopeManagerInstrumentation.java b/apm-agent-plugins/apm-opentracing-plugin/src/main/java/co/elastic/apm/agent/opentracing/impl/ScopeManagerInstrumentation.java index 0136c0f169..f537aa52da 100644 --- a/apm-agent-plugins/apm-opentracing-plugin/src/main/java/co/elastic/apm/agent/opentracing/impl/ScopeManagerInstrumentation.java +++ b/apm-agent-plugins/apm-opentracing-plugin/src/main/java/co/elastic/apm/agent/opentracing/impl/ScopeManagerInstrumentation.java @@ -25,7 +25,7 @@ package co.elastic.apm.agent.opentracing.impl; import co.elastic.apm.agent.bci.VisibleForAdvice; -import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToReturn; +import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignTo; import co.elastic.apm.agent.impl.transaction.AbstractSpan; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.method.MethodDescription; @@ -77,7 +77,7 @@ public CurrentSpanInstrumentation() { } @Nullable - @AssignToReturn + @AssignTo.Return @VisibleForAdvice @Advice.OnMethodExit(suppress = Throwable.class) public static Object getCurrentSpan() { @@ -96,7 +96,7 @@ public CurrentTraceContextInstrumentation() { } @Nullable - @AssignToReturn + @AssignTo.Return @VisibleForAdvice @Advice.OnMethodExit(suppress = Throwable.class) public static Object getCurrentTraceContext() { diff --git a/apm-agent-plugins/apm-opentracing-plugin/src/main/java/co/elastic/apm/agent/opentracing/impl/SpanContextInstrumentation.java b/apm-agent-plugins/apm-opentracing-plugin/src/main/java/co/elastic/apm/agent/opentracing/impl/SpanContextInstrumentation.java index 98cce76aa1..c767909f58 100644 --- a/apm-agent-plugins/apm-opentracing-plugin/src/main/java/co/elastic/apm/agent/opentracing/impl/SpanContextInstrumentation.java +++ b/apm-agent-plugins/apm-opentracing-plugin/src/main/java/co/elastic/apm/agent/opentracing/impl/SpanContextInstrumentation.java @@ -25,7 +25,7 @@ package co.elastic.apm.agent.opentracing.impl; import co.elastic.apm.agent.bci.VisibleForAdvice; -import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToReturn; +import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignTo; import co.elastic.apm.agent.impl.transaction.AbstractSpan; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.method.MethodDescription; @@ -71,7 +71,7 @@ public BaggageItemsInstrumentation() { @Nullable - @AssignToReturn + @AssignTo.Return @Advice.OnMethodExit(suppress = Throwable.class) public static Iterable> baggageItems(@Advice.FieldValue(value = "traceContext", typing = Assigner.Typing.DYNAMIC) @Nullable AbstractSpan traceContext) { if (traceContext != null) { @@ -97,7 +97,7 @@ public ToTraceIdInstrumentation() { } @Nullable - @AssignToReturn + @AssignTo.Return @Advice.OnMethodExit(suppress = Throwable.class) public static String toTraceId(@Advice.FieldValue(value = "traceContext", typing = Assigner.Typing.DYNAMIC) @Nullable AbstractSpan traceContext) { if (traceContext == null) { @@ -114,7 +114,7 @@ public ToSpanIdInstrumentation() { } @Nullable - @AssignToReturn + @AssignTo.Return @Advice.OnMethodExit(suppress = Throwable.class) public static String toTraceId(@Advice.FieldValue(value = "traceContext", typing = Assigner.Typing.DYNAMIC) @Nullable AbstractSpan traceContext) { if (traceContext == null) { diff --git a/apm-agent-plugins/apm-process-plugin/src/main/java/co/elastic/apm/agent/process/CommonsExecAsyncInstrumentation.java b/apm-agent-plugins/apm-process-plugin/src/main/java/co/elastic/apm/agent/process/CommonsExecAsyncInstrumentation.java index 9dce6ead9a..4a26b80011 100644 --- a/apm-agent-plugins/apm-process-plugin/src/main/java/co/elastic/apm/agent/process/CommonsExecAsyncInstrumentation.java +++ b/apm-agent-plugins/apm-process-plugin/src/main/java/co/elastic/apm/agent/process/CommonsExecAsyncInstrumentation.java @@ -25,7 +25,7 @@ package co.elastic.apm.agent.process; import co.elastic.apm.agent.bci.ElasticApmInstrumentation; -import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToArgument; +import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignTo; import co.elastic.apm.agent.concurrent.JavaConcurrent; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.NamedElement; @@ -96,7 +96,7 @@ public boolean indyPlugin() { public static final class CommonsExecAdvice { @Nullable - @AssignToArgument(0) + @AssignTo.Argument(0) @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) public static Runnable onEnter(Runnable runnable) { return JavaConcurrent.withContext(runnable, tracer); diff --git a/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/AsyncInstrumentation.java b/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/AsyncInstrumentation.java index af0cb28e01..d2c889f497 100644 --- a/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/AsyncInstrumentation.java +++ b/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/AsyncInstrumentation.java @@ -24,9 +24,9 @@ */ package co.elastic.apm.agent.servlet; -import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToArgument; -import co.elastic.apm.agent.servlet.helper.AsyncContextAdviceHelperImpl; +import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignTo; import co.elastic.apm.agent.concurrent.JavaConcurrent; +import co.elastic.apm.agent.servlet.helper.AsyncContextAdviceHelperImpl; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.NamedElement; import net.bytebuddy.description.method.MethodDescription; @@ -141,7 +141,7 @@ public Class getAdviceClass() { public static class AsyncContextStartAdvice { @Nullable - @AssignToArgument(0) + @AssignTo.Argument(0) @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) public static Runnable onEnterAsyncContextStart(@Advice.Argument(0) @Nullable Runnable runnable) { return JavaConcurrent.withContext(runnable, tracer); diff --git a/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/RequestStreamRecordingInstrumentation.java b/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/RequestStreamRecordingInstrumentation.java index 0f043cafe6..3f249ca45e 100644 --- a/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/RequestStreamRecordingInstrumentation.java +++ b/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/RequestStreamRecordingInstrumentation.java @@ -24,7 +24,7 @@ */ package co.elastic.apm.agent.servlet; -import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignToReturn; +import co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignTo; import co.elastic.apm.agent.impl.transaction.Transaction; import co.elastic.apm.agent.servlet.helper.RecordingServletInputStreamWrapper; import co.elastic.apm.agent.util.CallDepth; @@ -84,7 +84,7 @@ public static void onReadEnter(@Advice.This Object thiz) { } @Nullable - @AssignToReturn + @AssignTo.Return @Advice.OnMethodExit(suppress = Throwable.class, inline = false, onThrowable = Throwable.class) public static ServletInputStream afterGetInputStream(@Advice.Return @Nullable ServletInputStream inputStream) { if (callDepth.isNestedCallAndDecrement() || tracer == null || inputStream == null) { From d47f768532a5b38005280eca6212f70eb6f1542c Mon Sep 17 00:00:00 2001 From: Felix Barnsteiner Date: Thu, 18 Jun 2020 14:46:28 +0200 Subject: [PATCH 22/35] Update Byte Buddy to 1.10.12 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7e63d1ec46..0d436f68e2 100644 --- a/pom.xml +++ b/pom.xml @@ -96,7 +96,7 @@ 5.0.15.RELEASE 9.4.11.v20180605 0.1.19 - 1.10.12-SNAPSHOT + 1.10.12 1.17 From d4d09b21d63524da8f1a32a4707b8a9b534230ce Mon Sep 17 00:00:00 2001 From: Felix Barnsteiner Date: Fri, 19 Jun 2020 08:55:49 +0200 Subject: [PATCH 23/35] Fix JMS tests allow agent classes to be instrumented --- .../co/elastic/apm/agent/bci/ElasticApmAgent.java | 3 --- .../java/co/elastic/apm/agent/MockReporter.java | 5 +++++ .../jms/JmsMessageConsumerInstrumentation.java | 6 ------ .../apm/agent/jms/JmsInstrumentationIT.java | 15 ++++++--------- docs/configuration.asciidoc | 4 ++-- 5 files changed, 13 insertions(+), 20 deletions(-) diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/ElasticApmAgent.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/ElasticApmAgent.java index ca22255c9b..b0efdee38e 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/ElasticApmAgent.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/ElasticApmAgent.java @@ -56,7 +56,6 @@ import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.dynamic.ClassFileLocator; import net.bytebuddy.dynamic.DynamicType; -import net.bytebuddy.dynamic.loading.ByteArrayClassLoader; import net.bytebuddy.dynamic.scaffold.TypeValidation; import net.bytebuddy.matcher.ElementMatcher; import net.bytebuddy.matcher.ElementMatchers; @@ -503,8 +502,6 @@ public Iterable>> onError(int index, List> batc ? new SoftlyReferencingTypePoolCache(TypePool.Default.ReaderMode.FAST, 1, isReflectionClassLoader()) : AgentBuilder.PoolStrategy.Default.FAST) .ignore(any(), isReflectionClassLoader()) - // avoids instrumenting classes from helper class loaders - .or(any(), classLoaderWithName(ByteArrayClassLoader.ChildFirst.class.getName())) .or(any(), classLoaderWithName("org.codehaus.groovy.runtime.callsite.CallSiteClassLoader")) .or(nameStartsWith("co.elastic.apm.agent.shaded")) .or(nameStartsWith("org.aspectj.")) diff --git a/apm-agent-core/src/test/java/co/elastic/apm/agent/MockReporter.java b/apm-agent-core/src/test/java/co/elastic/apm/agent/MockReporter.java index 11be2ca7f0..74f028572c 100644 --- a/apm-agent-core/src/test/java/co/elastic/apm/agent/MockReporter.java +++ b/apm-agent-core/src/test/java/co/elastic/apm/agent/MockReporter.java @@ -239,6 +239,11 @@ public void assertNoSpan(long timeoutMs) { assertNoSpan(); } + public void awaitTransactionCount(int count) { + awaitTimeout(1000) + .untilAsserted(() -> assertThat(getTransactions()).hasSize(count)); + } + public void awaitSpanCount(int count) { awaitTimeout(1000) .untilAsserted(() -> assertThat(getSpans()).hasSize(count)); diff --git a/apm-agent-plugins/apm-jms-plugin/src/main/java/co/elastic/apm/agent/jms/JmsMessageConsumerInstrumentation.java b/apm-agent-plugins/apm-jms-plugin/src/main/java/co/elastic/apm/agent/jms/JmsMessageConsumerInstrumentation.java index e73d8259cc..8976b7b8ce 100644 --- a/apm-agent-plugins/apm-jms-plugin/src/main/java/co/elastic/apm/agent/jms/JmsMessageConsumerInstrumentation.java +++ b/apm-agent-plugins/apm-jms-plugin/src/main/java/co/elastic/apm/agent/jms/JmsMessageConsumerInstrumentation.java @@ -274,12 +274,6 @@ public static MessageListener beforeSetListener(@Advice.Argument(0) @Nullable Me } return original; } - - @Advice.OnMethodExit - private static void onExit() { - // workaround for https://github.com/raphw/byte-buddy/issues/874#issuecomment-637616829 - } - } } } diff --git a/apm-agent-plugins/apm-jms-plugin/src/test/java/co/elastic/apm/agent/jms/JmsInstrumentationIT.java b/apm-agent-plugins/apm-jms-plugin/src/test/java/co/elastic/apm/agent/jms/JmsInstrumentationIT.java index 71ed0e84b9..deb9c8e9d0 100644 --- a/apm-agent-plugins/apm-jms-plugin/src/test/java/co/elastic/apm/agent/jms/JmsInstrumentationIT.java +++ b/apm-agent-plugins/apm-jms-plugin/src/test/java/co/elastic/apm/agent/jms/JmsInstrumentationIT.java @@ -51,7 +51,6 @@ import javax.jms.TemporaryQueue; import javax.jms.TextMessage; import javax.jms.Topic; - import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; @@ -67,6 +66,8 @@ import java.util.function.Function; import java.util.stream.Collectors; +import static co.elastic.apm.agent.configuration.MessagingConfiguration.Strategy.BOTH; +import static co.elastic.apm.agent.configuration.MessagingConfiguration.Strategy.POLLING; import static co.elastic.apm.agent.jms.JmsInstrumentationHelper.JMS_EXPIRATION_HEADER; import static co.elastic.apm.agent.jms.JmsInstrumentationHelper.JMS_MESSAGE_ID_HEADER; import static co.elastic.apm.agent.jms.JmsInstrumentationHelper.JMS_TIMESTAMP_HEADER; @@ -74,11 +75,7 @@ import static co.elastic.apm.agent.jms.JmsInstrumentationHelper.MESSAGING_TYPE; import static co.elastic.apm.agent.jms.JmsInstrumentationHelperImpl.TEMP; import static co.elastic.apm.agent.jms.JmsInstrumentationHelperImpl.TIBCO_TMP_QUEUE_PREFIX; -import static co.elastic.apm.agent.configuration.MessagingConfiguration.Strategy.BOTH; -import static co.elastic.apm.agent.configuration.MessagingConfiguration.Strategy.POLLING; -import static java.util.concurrent.TimeUnit.MILLISECONDS; import static org.assertj.core.api.Assertions.assertThat; -import static org.awaitility.Awaitility.await; import static org.mockito.Mockito.doReturn; @RunWith(Parameterized.class) @@ -96,7 +93,7 @@ public class JmsInstrumentationIT extends AbstractInstrumentationTest { private Queue noopQ; - public JmsInstrumentationIT(BrokerFacade brokerFacade) throws Exception { + public JmsInstrumentationIT(BrokerFacade brokerFacade, Class clazz) throws Exception { this.brokerFacade = brokerFacade; if (staticBrokerFacade.add(brokerFacade)) { brokerFacade.prepareResources(); @@ -104,9 +101,9 @@ public JmsInstrumentationIT(BrokerFacade brokerFacade) throws Exception { coreConfiguration = config.getConfig(CoreConfiguration.class); } - @Parameterized.Parameters(name = "BrokerFacade={0}") + @Parameterized.Parameters(name = "BrokerFacade={1}") public static Iterable brokerFacades() { - return Arrays.asList(new Object[][]{{new ActiveMqFacade()}, {new ActiveMqArtemisFacade()}}); + return Arrays.asList(new Object[][]{{new ActiveMqFacade(), ActiveMqFacade.class}, {new ActiveMqArtemisFacade(), ActiveMqArtemisFacade.class}}); } @AfterClass @@ -452,7 +449,7 @@ private void verifyQueueSendReceiveOnNonTracedThread(Queue queue) } private void verifySendListenOnNonTracedThread(String destinationName, TextMessage message, int expectedReadTransactions) throws JMSException { - await().atMost(1000, MILLISECONDS).until(() -> reporter.getTransactions().size() == expectedReadTransactions); + reporter.awaitTransactionCount(expectedReadTransactions); List spans = reporter.getSpans(); assertThat(spans).hasSize(1); diff --git a/docs/configuration.asciidoc b/docs/configuration.asciidoc index 6ee8d57525..8df1c2da15 100644 --- a/docs/configuration.asciidoc +++ b/docs/configuration.asciidoc @@ -671,7 +671,7 @@ you should add an additional entry to this list (make sure to also include the d ==== `disable_instrumentations` (added[1.0.0,Changing this value at runtime is possible since version 1.15.0]) A list of instrumentations which should be disabled. -Valid options are `annotations`, `apache-commons-exec`, `apache-httpclient`, `asynchttpclient`, `concurrent`, `dubbo`, `elasticsearch-restclient`, `exception-handler`, `executor`, `executor-collection`, `experimental`, `grails`, `grpc`, `hibernate-search`, `http-client`, `jax-rs`, `jax-ws`, `jdbc`, `jedis`, `jms`, `jsf`, `kafka`, `lettuce`, `log4j`, `logging`, `mongodb-client`, `mule`, `okhttp`, `opentracing`, `process`, `public-api`, `quartz`, `redis`, `redisson`, `render`, `scheduled`, `servlet-api`, `servlet-api-async`, `servlet-input-stream`, `slf4j`, `spring-mvc`, `spring-resttemplate`, `spring-service-name`, `spring-view-render`, `ssl-context`, `urlconnection`. +Valid options are `annotations`, `apache-commons-exec`, `apache-httpclient`, `asynchttpclient`, `concurrent`, `dubbo`, `elasticsearch-restclient`, `exception-handler`, `executor`, `executor-collection`, `experimental`, `fork-join`, `grails`, `grpc`, `hibernate-search`, `http-client`, `jax-rs`, `jax-ws`, `jdbc`, `jedis`, `jms`, `jsf`, `kafka`, `lettuce`, `log4j`, `logging`, `mongodb-client`, `mule`, `okhttp`, `opentracing`, `process`, `public-api`, `quartz`, `redis`, `redisson`, `render`, `scheduled`, `servlet-api`, `servlet-api-async`, `servlet-input-stream`, `slf4j`, `spring-mvc`, `spring-resttemplate`, `spring-service-name`, `spring-view-render`, `ssl-context`, `urlconnection`. If you want to try out experimental features, set the value to an empty string. NOTE: Changing this value at runtime can slow down the application temporarily. @@ -2530,7 +2530,7 @@ The default unit for this option is `ms`. # sanitize_field_names=password,passwd,pwd,secret,*key,*token*,*session*,*credit*,*card*,authorization,set-cookie # A list of instrumentations which should be disabled. -# Valid options are `annotations`, `apache-commons-exec`, `apache-httpclient`, `asynchttpclient`, `concurrent`, `dubbo`, `elasticsearch-restclient`, `exception-handler`, `executor`, `executor-collection`, `experimental`, `grails`, `grpc`, `hibernate-search`, `http-client`, `jax-rs`, `jax-ws`, `jdbc`, `jedis`, `jms`, `jsf`, `kafka`, `lettuce`, `log4j`, `logging`, `mongodb-client`, `mule`, `okhttp`, `opentracing`, `process`, `public-api`, `quartz`, `redis`, `redisson`, `render`, `scheduled`, `servlet-api`, `servlet-api-async`, `servlet-input-stream`, `slf4j`, `spring-mvc`, `spring-resttemplate`, `spring-service-name`, `spring-view-render`, `ssl-context`, `urlconnection`. +# Valid options are `annotations`, `apache-commons-exec`, `apache-httpclient`, `asynchttpclient`, `concurrent`, `dubbo`, `elasticsearch-restclient`, `exception-handler`, `executor`, `executor-collection`, `experimental`, `fork-join`, `grails`, `grpc`, `hibernate-search`, `http-client`, `jax-rs`, `jax-ws`, `jdbc`, `jedis`, `jms`, `jsf`, `kafka`, `lettuce`, `log4j`, `logging`, `mongodb-client`, `mule`, `okhttp`, `opentracing`, `process`, `public-api`, `quartz`, `redis`, `redisson`, `render`, `scheduled`, `servlet-api`, `servlet-api-async`, `servlet-input-stream`, `slf4j`, `spring-mvc`, `spring-resttemplate`, `spring-service-name`, `spring-view-render`, `ssl-context`, `urlconnection`. # If you want to try out experimental features, set the value to an empty string. # # NOTE: Changing this value at runtime can slow down the application temporarily. From 3437036aef3deae030044b00eeb5ed28f38ad067 Mon Sep 17 00:00:00 2001 From: Felix Barnsteiner Date: Fri, 19 Jun 2020 09:43:27 +0200 Subject: [PATCH 24/35] Fix Kafka instrumentation --- .../agent/kafka/ConsumerRecordsIteratorInstrumentation.java | 4 ++-- .../agent/kafka/ConsumerRecordsRecordListInstrumentation.java | 4 ++-- .../agent/kafka/ConsumerRecordsRecordsInstrumentation.java | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/apm-agent-plugins/apm-kafka-plugin/apm-kafka-headers-plugin/src/main/java/co/elastic/apm/agent/kafka/ConsumerRecordsIteratorInstrumentation.java b/apm-agent-plugins/apm-kafka-plugin/apm-kafka-headers-plugin/src/main/java/co/elastic/apm/agent/kafka/ConsumerRecordsIteratorInstrumentation.java index aa1e108d30..18dc0e1083 100644 --- a/apm-agent-plugins/apm-kafka-plugin/apm-kafka-headers-plugin/src/main/java/co/elastic/apm/agent/kafka/ConsumerRecordsIteratorInstrumentation.java +++ b/apm-agent-plugins/apm-kafka-plugin/apm-kafka-headers-plugin/src/main/java/co/elastic/apm/agent/kafka/ConsumerRecordsIteratorInstrumentation.java @@ -70,7 +70,7 @@ public static class ConsumerRecordsAdvice { @Nullable @AssignTo.Return @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) - public static Iterator wrapIterator(@Nullable @Advice.Return Iterator iterator) { + public static Iterator wrapIterator(@Nullable @Advice.Return final Iterator iterator) { if (tracer == null || !tracer.isRunning() || tracer.currentTransaction() != null) { return iterator; } @@ -79,7 +79,7 @@ public static Iterator wrapIterator(@Nullable @Advice.Return Ite KafkaInstrumentationHeadersHelper kafkaInstrumentationHelper = kafkaInstrHeadersHelperManager.getForClassLoaderOfClass(KafkaProducer.class); if (iterator != null && kafkaInstrumentationHelper != null) { - iterator = kafkaInstrumentationHelper.wrapConsumerRecordIterator(iterator); + return kafkaInstrumentationHelper.wrapConsumerRecordIterator(iterator); } return iterator; } diff --git a/apm-agent-plugins/apm-kafka-plugin/apm-kafka-headers-plugin/src/main/java/co/elastic/apm/agent/kafka/ConsumerRecordsRecordListInstrumentation.java b/apm-agent-plugins/apm-kafka-plugin/apm-kafka-headers-plugin/src/main/java/co/elastic/apm/agent/kafka/ConsumerRecordsRecordListInstrumentation.java index 5a75aac356..0dcfbef421 100644 --- a/apm-agent-plugins/apm-kafka-plugin/apm-kafka-headers-plugin/src/main/java/co/elastic/apm/agent/kafka/ConsumerRecordsRecordListInstrumentation.java +++ b/apm-agent-plugins/apm-kafka-plugin/apm-kafka-headers-plugin/src/main/java/co/elastic/apm/agent/kafka/ConsumerRecordsRecordListInstrumentation.java @@ -71,7 +71,7 @@ public static class ConsumerRecordsAdvice { @Nullable @AssignTo.Return @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) - public static List wrapRecordList(@Nullable @Advice.Return List list) { + public static List wrapRecordList(@Nullable @Advice.Return final List list) { if (tracer == null || !tracer.isRunning() || tracer.currentTransaction() != null) { return list; } @@ -80,7 +80,7 @@ public static List wrapRecordList(@Nullable @Advice.Return List< KafkaInstrumentationHeadersHelper kafkaInstrumentationHelper = kafkaInstrHeadersHelperManager.getForClassLoaderOfClass(KafkaProducer.class); if (list != null && kafkaInstrumentationHelper != null) { - list = kafkaInstrumentationHelper.wrapConsumerRecordList(list); + return kafkaInstrumentationHelper.wrapConsumerRecordList(list); } return list; } diff --git a/apm-agent-plugins/apm-kafka-plugin/apm-kafka-headers-plugin/src/main/java/co/elastic/apm/agent/kafka/ConsumerRecordsRecordsInstrumentation.java b/apm-agent-plugins/apm-kafka-plugin/apm-kafka-headers-plugin/src/main/java/co/elastic/apm/agent/kafka/ConsumerRecordsRecordsInstrumentation.java index 43bdbf9dd9..49c0545c44 100644 --- a/apm-agent-plugins/apm-kafka-plugin/apm-kafka-headers-plugin/src/main/java/co/elastic/apm/agent/kafka/ConsumerRecordsRecordsInstrumentation.java +++ b/apm-agent-plugins/apm-kafka-plugin/apm-kafka-headers-plugin/src/main/java/co/elastic/apm/agent/kafka/ConsumerRecordsRecordsInstrumentation.java @@ -69,7 +69,7 @@ public static class ConsumerRecordsAdvice { @Nullable @AssignTo.Return @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) - public static Iterable wrapIterable(@Nullable @Advice.Return Iterable iterable) { + public static Iterable wrapIterable(@Nullable @Advice.Return final Iterable iterable) { if (tracer == null || !tracer.isRunning() || tracer.currentTransaction() != null) { return iterable; } @@ -78,7 +78,7 @@ public static Iterable wrapIterable(@Nullable @Advice.Return Ite KafkaInstrumentationHeadersHelper kafkaInstrumentationHelper = kafkaInstrHeadersHelperManager.getForClassLoaderOfClass(KafkaProducer.class); if (iterable != null && kafkaInstrumentationHelper != null) { - iterable = kafkaInstrumentationHelper.wrapConsumerRecordIterable(iterable); + return kafkaInstrumentationHelper.wrapConsumerRecordIterable(iterable); } return iterable; } From 0d10839c45e6f12c13926837c762e19c20de1ead Mon Sep 17 00:00:00 2001 From: Felix Barnsteiner Date: Fri, 19 Jun 2020 10:58:58 +0200 Subject: [PATCH 25/35] Fix ITs by lazily resolving type descriptions --- .../apm/agent/bci/ElasticApmAgent.java | 2 +- .../apm/agent/bci/HelperClassManager.java | 2 +- .../apm/agent/bci/InstrumentationTest.java | 77 +++++++++++++++++++ 3 files changed, 79 insertions(+), 2 deletions(-) diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/ElasticApmAgent.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/ElasticApmAgent.java index b0efdee38e..4141a7e526 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/ElasticApmAgent.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/ElasticApmAgent.java @@ -352,7 +352,7 @@ public boolean matches(MethodDescription target) { * @param adviceClassName the name of the advice class */ private static void validateAdvice(String adviceClassName) { - TypePool pool = new TypePool.Default(TypePool.CacheProvider.NoOp.INSTANCE, ClassFileLocator.ForClassLoader.ofSystemLoader(), TypePool.Default.ReaderMode.FAST); + TypePool pool = new TypePool.Default.WithLazyResolution(TypePool.CacheProvider.NoOp.INSTANCE, ClassFileLocator.ForClassLoader.ofSystemLoader(), TypePool.Default.ReaderMode.FAST); TypeDescription typeDescription = pool.describe(adviceClassName).resolve(); for (MethodDescription.InDefinedShape enterAdvice : typeDescription.getDeclaredMethods().filter(isStatic().and(isAnnotatedWith(Advice.OnMethodEnter.class)))) { validateAdviceReturnAndParameterTypes(enterAdvice); diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/HelperClassManager.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/HelperClassManager.java index 65296509d1..1b9a50b592 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/HelperClassManager.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/HelperClassManager.java @@ -299,7 +299,7 @@ public synchronized static ClassLoader getOrCreatePluginClassLoader(@Nullable Cl } List classesToInjectCopy = new ArrayList<>(classesToInject.size()); - TypePool pool = new TypePool.Default(TypePool.CacheProvider.NoOp.INSTANCE, ClassFileLocator.ForClassLoader.ofSystemLoader(), TypePool.Default.ReaderMode.FAST); + TypePool pool = new TypePool.Default.WithLazyResolution(TypePool.CacheProvider.NoOp.INSTANCE, ClassFileLocator.ForClassLoader.ofSystemLoader(), TypePool.Default.ReaderMode.FAST); for (Iterator iterator = classesToInject.iterator(); iterator.hasNext(); ) { String className = iterator.next(); if (!exclusionMatcher.matches(pool.describe(className).resolve())) { diff --git a/apm-agent-core/src/test/java/co/elastic/apm/agent/bci/InstrumentationTest.java b/apm-agent-core/src/test/java/co/elastic/apm/agent/bci/InstrumentationTest.java index 65cee9ccdf..00c0e3177b 100644 --- a/apm-agent-core/src/test/java/co/elastic/apm/agent/bci/InstrumentationTest.java +++ b/apm-agent-core/src/test/java/co/elastic/apm/agent/bci/InstrumentationTest.java @@ -31,6 +31,7 @@ import co.elastic.apm.agent.configuration.SpyConfiguration; import co.elastic.apm.agent.impl.ElasticApmTracer; import co.elastic.apm.agent.impl.ElasticApmTracerBuilder; +import co.elastic.apm.agent.impl.transaction.Span; import co.elastic.apm.agent.matcher.WildcardMatcher; import co.elastic.apm.agent.util.GlobalVariables; import net.bytebuddy.agent.ByteBuddyAgent; @@ -383,6 +384,22 @@ void testAdviceInSubpackage() { .isInstanceOf(IllegalStateException.class); } + @Test + void testAdviceWithAgentReturnType() { + assertThatThrownBy(() -> ElasticApmAgent.initInstrumentation(tracer, + ByteBuddyAgent.install(), + Collections.singletonList(new AgentTypeReturnInstrumentation()))) + .isInstanceOf(IllegalStateException.class); + } + + @Test + void testAdviceWithAgentParameterType() { + assertThatThrownBy(() -> ElasticApmAgent.initInstrumentation(tracer, + ByteBuddyAgent.install(), + Collections.singletonList(new AgentTypeParameterInstrumentation()))) + .isInstanceOf(IllegalStateException.class); + } + @Nullable public ClassLoader getPluginClassLoader() { return null; @@ -818,4 +835,64 @@ public boolean indyPlugin() { return true; } } + + public static class AgentTypeReturnInstrumentation extends ElasticApmInstrumentation { + + @Advice.OnMethodEnter(inline = false) + public static Span onEnter() { + return null; + } + + @Override + public ElementMatcher getTypeMatcher() { + return none(); + } + + @Override + public ElementMatcher getMethodMatcher() { + return none(); + } + + @Override + public Collection getInstrumentationGroupNames() { + return Collections.singletonList("test"); + } + + @Override + public boolean indyPlugin() { + return true; + } + } + + public static class AgentTypeParameterInstrumentation extends ElasticApmInstrumentation { + + @Advice.OnMethodEnter(inline = false) + public static Object onEnter() { + return null; + } + + @Advice.OnMethodExit(inline = false) + private static void onExit(@Advice.Enter Span span) { + } + + @Override + public ElementMatcher getTypeMatcher() { + return none(); + } + + @Override + public ElementMatcher getMethodMatcher() { + return none(); + } + + @Override + public Collection getInstrumentationGroupNames() { + return Collections.singletonList("test"); + } + + @Override + public boolean indyPlugin() { + return true; + } + } } From b1c69b4005a2339ca711403279a724a54429daed Mon Sep 17 00:00:00 2001 From: Felix Barnsteiner Date: Fri, 19 Jun 2020 11:16:16 +0200 Subject: [PATCH 26/35] Fix another test --- .../src/main/java/co/elastic/apm/agent/bci/ElasticApmAgent.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/ElasticApmAgent.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/ElasticApmAgent.java index 4141a7e526..273ac2d87f 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/ElasticApmAgent.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/ElasticApmAgent.java @@ -384,7 +384,7 @@ private static void validateAdviceReturnAndParameterTypes(MethodDescription.InDe throw new IllegalStateException("Advice return type must not be an agent type: " + advice.toGenericString()); } for (ParameterDescription.InDefinedShape parameter : advice.getParameters()) { - if (parameter.getName().startsWith("co.elastic.apm")) { + if (parameter.getType().getTypeName().startsWith("co.elastic.apm")) { throw new IllegalStateException("Advice parameters must not contain an agent type: " + advice.toGenericString()); } } From 8c8b5f445eedc1330161d3d191048c8acda2fa18 Mon Sep 17 00:00:00 2001 From: Felix Barnsteiner Date: Fri, 19 Jun 2020 15:58:13 +0200 Subject: [PATCH 27/35] Fix bootstrap method signature --- .../elastic/apm/agent/bci/IndyBootstrap.java | 55 ++++++++++-------- .../bootstrap/IndyBootstrapDispatcher.clazz | Bin 3403 -> 2899 bytes .../bootstrap/IndyBootstrapDispatcher.java | 11 +--- .../concurrent/ExecutorInstrumentation.java | 7 ++- .../co/elastic/apm/servlet/AgentFileIT.java | 1 + 5 files changed, 39 insertions(+), 35 deletions(-) diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/IndyBootstrap.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/IndyBootstrap.java index 47828a82c9..4f6ce04170 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/IndyBootstrap.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/IndyBootstrap.java @@ -27,6 +27,7 @@ import co.elastic.apm.agent.util.PackageScanner; import net.bytebuddy.asm.Advice; import net.bytebuddy.dynamic.loading.ClassInjector; +import org.slf4j.LoggerFactory; import org.stagemonitor.util.IOUtils; import javax.annotation.Nullable; @@ -172,10 +173,8 @@ public static Method getIndyBootstrapMethod() { Class indyBootstrapClass = initIndyBootstrap(); indyBootstrapClass .getField("bootstrap") - .set(null, IndyBootstrap.class.getMethod("bootstrap", MethodHandles.Lookup.class, String.class, MethodType.class, - String.class, int.class, Class.class, String.class, Object[].class)); - return indyBootstrapMethod = indyBootstrapClass.getMethod("bootstrap", MethodHandles.Lookup.class, String.class, - MethodType.class, String.class, int.class, Class.class, String.class, Object[].class); + .set(null, IndyBootstrap.class.getMethod("bootstrap", MethodHandles.Lookup.class, String.class, MethodType.class, Object[].class)); + return indyBootstrapMethod = indyBootstrapClass.getMethod("bootstrap", MethodHandles.Lookup.class, String.class, MethodType.class, Object[].class); } catch (Exception e) { throw new RuntimeException(e); } @@ -219,30 +218,38 @@ private static Class initIndyBootstrap() throws Exception { * Exceptions and {@code null} return values are handled by {@code java.lang.IndyBootstrapDispatcher#bootstrap}. *

*/ + @Nullable public static ConstantCallSite bootstrap(MethodHandles.Lookup lookup, String adviceMethodName, MethodType adviceMethodType, - String adviceClassName, - int enter, - Class instrumentedType, - String instrumentedMethodName, Object... args) throws Exception { - Class adviceClass = Class.forName(adviceClassName); - String packageName = adviceClass.getPackage().getName(); - List pluginClasses = classesByPackage.get(packageName); - if (pluginClasses == null) { - classesByPackage.putIfAbsent(packageName, PackageScanner.getClassNames(packageName)); - pluginClasses = classesByPackage.get(packageName); + try { + String adviceClassName = (String) args[0]; + int enter = (Integer) args[1]; + Class instrumentedType = (Class) args[2]; + String instrumentedMethodName = (String) args[3]; + MethodHandle instrumentedMethod = args.length >= 5 ? (MethodHandle) args[4] : null; + Class adviceClass = Class.forName(adviceClassName); + String packageName = adviceClass.getPackage().getName(); + List pluginClasses = classesByPackage.get(packageName); + if (pluginClasses == null) { + classesByPackage.putIfAbsent(packageName, PackageScanner.getClassNames(packageName)); + pluginClasses = classesByPackage.get(packageName); + } + ClassLoader pluginClassLoader = HelperClassManager.ForIndyPlugin.getOrCreatePluginClassLoader( + lookup.lookupClass().getClassLoader(), + pluginClasses, + isAnnotatedWith(named(GlobalState.class.getName())) + // no plugin CL necessary as all types are available form bootstrap CL + // also, this plugin is used as a dependency in other plugins + .or(nameStartsWith("co.elastic.apm.agent.concurrent"))); + Class adviceInPluginCL = pluginClassLoader.loadClass(adviceClassName); + MethodHandle methodHandle = MethodHandles.lookup().findStatic(adviceInPluginCL, adviceMethodName, adviceMethodType); + return new ConstantCallSite(methodHandle); + } catch (Exception e) { + // must not be a static field as it would initialize logging before it's ready + LoggerFactory.getLogger(IndyBootstrap.class).error(e.getMessage(), e); + return null; } - ClassLoader pluginClassLoader = HelperClassManager.ForIndyPlugin.getOrCreatePluginClassLoader( - lookup.lookupClass().getClassLoader(), - pluginClasses, - isAnnotatedWith(named(GlobalState.class.getName())) - // no plugin CL necessary as all types are available form bootstrap CL - // also, this plugin is used as a dependency in other plugins - .or(nameStartsWith("co.elastic.apm.agent.concurrent"))); - Class adviceInPluginCL = pluginClassLoader.loadClass(adviceClassName); - MethodHandle methodHandle = MethodHandles.lookup().findStatic(adviceInPluginCL, adviceMethodName, adviceMethodType); - return new ConstantCallSite(methodHandle); } } diff --git a/apm-agent-core/src/main/resources/bootstrap/IndyBootstrapDispatcher.clazz b/apm-agent-core/src/main/resources/bootstrap/IndyBootstrapDispatcher.clazz index 989ce9d357363abc3564cafb0aad67e9662c15f7..4528ca7da9ff6f8df256c44a6451d4b6b3f96ca3 100644 GIT binary patch delta 1050 zcmYk5+iz4=6vlsh&SlS>o^Wul239*Iw&e>-^FAXR-GC ztsk!gJNZ1qIDBlhxC#M6``j&s6^Nf&a` zVb5495gT~c8P8ds&$7=sUx?^q%JO1F2QL|Nr>fPjzPQ#kZ3vH4tEbP-8RA2;vorJK zXQmexW)=+R({Hl9X-}9-Djz2Udz zUX6K;*9}Q}+a&3Zpkwq6=N{rs%juXi499fQ9rG5mmTJr#Z(HVL7I-J-Ebqp=$NMoK z@L_r`=vVTOVm{`R^m`eL)MC!@Y5G^t>-F$idMo&H<(F{Es})2bWXQQfq;3U(^%#i+HE)Vz-icdh!94Ld{ zVUKE*``5ze8hZkp>a^wy`8ExxPH~m?6{01YC$7>_(aI!0U2e3{s#ijl?bt4=5ICHt tl{Oiz;-R2*DXuM3yPJ5XOJAx3e=l2@8ZJLnKHDmjD4xLWG4x43T6*27##*RVlU4kP8oP)%NuHPIsU0>*+Ip#r~|w|MAZc zKLPvs%EC{-Ng1aU0~XKmyu}M76@!WwEfl9MdU(m?3@IJ6CHJ!Dk)?ljz3+38*|+DlJPr03JE zlj-beYce-8o*nH>xY2OU+IL2gal9x!GCw|?>6uK=&Ynt7WekFuY%VinU?dEp?mnQ?{y%g! zK^~E=#>1VPunpGkx5hp<=|9p`#p51r$=)MX zs&y%evjtl+;^9?%zi~{g5e@%{Kjm*srUFgLRIoXj3hhm%R9T=vAejm~!DK2LazbL2 zqmt_ceb$1WD2aCkHNtf5FMT#URNTcMMWTbGj^w{F! zL+=cIQoBWV7bw5KyC|J*mAHuRWnPYYQXFyR!2MqHm*mJc-EYAT3Z`G=k{sVII<(gz z1%sX!m)(%(bqXaVkvBQSVZuBuXiREs5a<>H8=2jb`Hc7n3H>XY&Ag&caYWH0)=(Vv z7SAd@6ABf&M}>eT_lPQb|CW-+B-tmbO%l>dnIc&`^4C9OMH{cz8za>%OinxuoA!ne zstar@DJzM|%P#Due4XuUM3&jocN-_wc#Dd=D_$Q;g#*?A& Zofev=Q0f!N$4MIGX-R&aeA()8{sWM@?qC1_ diff --git a/apm-agent-core/src/main/resources/bootstrap/IndyBootstrapDispatcher.java b/apm-agent-core/src/main/resources/bootstrap/IndyBootstrapDispatcher.java index cc0bd1ddfd..c19c995426 100644 --- a/apm-agent-core/src/main/resources/bootstrap/IndyBootstrapDispatcher.java +++ b/apm-agent-core/src/main/resources/bootstrap/IndyBootstrapDispatcher.java @@ -24,21 +24,14 @@ public class IndyBootstrapDispatcher { public static CallSite bootstrap(MethodHandles.Lookup lookup, String adviceMethodName, MethodType adviceMethodType, - String adviceClassName, - int enter, - Class instrumentedType, - String instrumentedMethodName, Object... args) { CallSite callSite = null; if (bootstrap != null) { try { - callSite = (CallSite) bootstrap.invoke(null, lookup, + callSite = (CallSite) bootstrap.invoke(null, + lookup, adviceMethodName, adviceMethodType, - adviceClassName, - enter, - instrumentedType, - instrumentedMethodName, args); } catch (Exception e) { e.printStackTrace(); diff --git a/apm-agent-plugins/apm-java-concurrent-plugin/src/main/java/co/elastic/apm/agent/concurrent/ExecutorInstrumentation.java b/apm-agent-plugins/apm-java-concurrent-plugin/src/main/java/co/elastic/apm/agent/concurrent/ExecutorInstrumentation.java index ac33bff9f3..582f0f90fb 100644 --- a/apm-agent-plugins/apm-java-concurrent-plugin/src/main/java/co/elastic/apm/agent/concurrent/ExecutorInstrumentation.java +++ b/apm-agent-plugins/apm-java-concurrent-plugin/src/main/java/co/elastic/apm/agent/concurrent/ExecutorInstrumentation.java @@ -87,8 +87,11 @@ public ElementMatcher getTypeMatcher() { return hasSuperType(named("java.util.concurrent.Executor")) // executes on same thread, no need to wrap to activate again .and(not(named("org.apache.felix.resolver.ResolverImpl$DumbExecutor"))) - .and(not(nameEndsWith("org.eclipse.jetty.util.thread.QueuedThreadPool"))) - .and(not(nameContains(".netty."))) + .and(not(nameContains("jetty"))) + .and(not(nameContains("tomcat"))) + .and(not(nameContains("jboss"))) + .and(not(nameContains("undertow"))) + .and(not(nameContains("netty"))) // hazelcast tries to serialize the Runnables/Callables to execute them on remote JVMs .and(not(nameStartsWith("com.hazelcast"))) .and(not(isProxy())); diff --git a/integration-tests/application-server-integration-tests/src/test/java/co/elastic/apm/servlet/AgentFileIT.java b/integration-tests/application-server-integration-tests/src/test/java/co/elastic/apm/servlet/AgentFileIT.java index b0820e67bc..a90d412847 100644 --- a/integration-tests/application-server-integration-tests/src/test/java/co/elastic/apm/servlet/AgentFileIT.java +++ b/integration-tests/application-server-integration-tests/src/test/java/co/elastic/apm/servlet/AgentFileIT.java @@ -67,6 +67,7 @@ public void testEverythingIsShaded() throws IOException { .filter(entry -> !entry.startsWith("co/")) .filter(entry -> !entry.startsWith("schema/")) .filter(entry -> !entry.startsWith("asyncprofiler/")) + .filter(entry -> !entry.startsWith("bootstrap/")) .filter(entry -> !entry.startsWith("ElasticApmLog4j-")) .filter(entry -> !entry.startsWith("elasticapmlog4j2.component.properties"))) .isEmpty(); From 960146aad8bcb08e54c5ccf4163eb88420b2bdab Mon Sep 17 00:00:00 2001 From: Felix Barnsteiner Date: Mon, 22 Jun 2020 14:59:59 +0200 Subject: [PATCH 28/35] Fix Payara and WebSphere tests - Don't start agent on early J9 versions that crash on indy bootstrap - Fix location strategy order to avoid ClassNotFoundExceptions on Payara --- .../co/elastic/apm/agent/bci/AgentMain.java | 73 +++++++++++++------ .../apm/agent/bci/ElasticApmAgent.java | 16 ++-- .../apm/agent/configuration/StartupInfo.java | 7 +- .../elastic/apm/agent/bci/AgentMainTest.java | 10 ++- .../latest/testapp/generated/HelloGrpc.java | 2 +- docs/supported-technologies.asciidoc | 2 +- .../co/elastic/apm/servlet/WebSphereIT.java | 7 +- .../src/main/webapp/WEB-INF/web.xml | 8 +- .../src/main/webapp/WEB-INF/web.xml | 6 +- .../src/main/webapp/WEB-INF/web.xml | 8 +- .../soap-test/src/main/webapp/WEB-INF/web.xml | 7 +- 11 files changed, 99 insertions(+), 47 deletions(-) diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/AgentMain.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/AgentMain.java index 920a7634f2..0f7a82a889 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/AgentMain.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/AgentMain.java @@ -24,6 +24,7 @@ */ package co.elastic.apm.agent.bci; +import javax.annotation.Nullable; import java.io.File; import java.lang.instrument.Instrumentation; import java.net.URISyntaxException; @@ -75,12 +76,13 @@ public synchronized static void init(String agentArguments, Instrumentation inst String javaVersion = System.getProperty("java.version"); String javaVmName = System.getProperty("java.vm.name"); - if (!isJavaVersionSupported(javaVersion, javaVmName)) { + String javaVmVersion = System.getProperty("java.vm.version"); + if (!isJavaVersionSupported(javaVersion, javaVmName, javaVmVersion)) { // Gracefully abort agent startup is better than unexpected failure down the road when we known a given JVM // version is not supported. Agent might trigger known JVM bugs causing JVM crashes, notably on early Java 8 // versions (but fixed in later versions), given those versions are obsolete and agent can't have workarounds // for JVM internals, there is no other option but to use an up-to-date JVM instead. - System.err.println(String.format("Failed to start agent - JVM version not supported: %s", javaVersion)); + System.err.println(String.format("Failed to start agent - JVM version not supported: %s %s %s", javaVersion, javaVmName, javaVmVersion)); return; } @@ -111,36 +113,59 @@ public synchronized static void init(String agentArguments, Instrumentation inst * unknown version format, we assume it's supported, thus this method might return false positives, but never false * negatives. * - * @param version jvm version, from {@code System.getProperty("java.version")} - * @param vmName jvm name, from {@code System.getProperty("java.vm.name")} + * @param version jvm version, from {@code System.getProperty("java.version")} + * @param vmName jvm name, from {@code System.getProperty("java.vm.name")} + * @param vmVersion jvm version, from {@code System.getProperty("java.vm.version")} * @return true if the version is supported, false otherwise */ // package-protected for testing - static boolean isJavaVersionSupported(String version, String vmName) { - boolean postJsr223 = !version.startsWith("1."); + static boolean isJavaVersionSupported(String version, String vmName, @Nullable String vmVersion) { // new scheme introduced in java 9, thus we can use it as a shortcut - if (postJsr223) { - return true; + int major; + if (version.startsWith("1.")) { + major = Character.digit(version.charAt(2), 10); + } else { + major = Integer.parseInt(version.split("\\.")[0]); } - char major = version.charAt(2); - if (major < '7') { + boolean isHotSpot = vmName.contains("HotSpot(TM)") || vmName.contains("OpenJDK"); + boolean isIbmJ9 = vmName.contains("IBM J9"); + if (major < 7) { // given code is compiled with java 7, this one is unlikely in practice return false; - } else if (!vmName.contains("HotSpot(TM)")) { - // non-hotspot JVMs are not concerned (yet) - return true; - } else if (major == '7') { - // HotSpot 7 - // versions prior to that have unreliable invoke dynamic support according to - // https://groovy-lang.org/indy.html - return isUpdateVersionAtLeast(version, 60); - } else if (major == '8' ){ - // HotSpot 8 - return isUpdateVersionAtLeast(version, 40); - } else { - // > 8 - return true; + } + if (isHotSpot) { + return isHotSpotVersionSupported(version, major); + } else if (isIbmJ9) { + return isIbmJ9VersionSupported(vmVersion, major); + } + // innocent until proven guilty + return true; + } + + private static boolean isHotSpotVersionSupported(String version, int major) { + switch (major) { + case 7: + // versions prior to that have unreliable invoke dynamic support according to https://groovy-lang.org/indy.html + return isUpdateVersionAtLeast(version, 60); + case 8: + return isUpdateVersionAtLeast(version, 40); + default: + return true; + } + } + + private static boolean isIbmJ9VersionSupported(@Nullable String vmVersion, int major) { + switch (major) { + case 7: + return false; + case 8: + // early versions crash during invokedynamic bootstrap + // the exact version that fixes that error is currently not known + // presumably, service refresh 5 (build 2.8) fixes the issue + return !"2.8".equals(vmVersion); + default: + return true; } } diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/ElasticApmAgent.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/ElasticApmAgent.java index 273ac2d87f..c0c85ab282 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/ElasticApmAgent.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/ElasticApmAgent.java @@ -472,11 +472,17 @@ private static AgentBuilder getAgentBuilder(final ByteBuddy byteBuddy, final Cor AgentBuilder.LocationStrategy locationStrategy = AgentBuilder.LocationStrategy.ForClassLoader.WEAK; if (agentJarFile != null) { try { - locationStrategy = - ((AgentBuilder.LocationStrategy.ForClassLoader) locationStrategy).withFallbackTo( - ClassFileLocator.ForJarFile.of(agentJarFile), - new RootPackageCustomLocator("java.", ClassFileLocator.ForClassLoader.ofBootLoader()) - ); + locationStrategy = new AgentBuilder.LocationStrategy.Compound( + // it's important to first try loading from the agent jar and not the class loader of the instrumented class + // the latter may not have access to the agent resources: + // when adding the agent to the bootstrap CL (appendToBootstrapClassLoaderSearch) + // the bootstrap CL can load its classes but not its resources + // the application class loader may cache the fact that a resource like AbstractSpan.class can't be resolved + // and also refuse to load the class + new AgentBuilder.LocationStrategy.Simple(ClassFileLocator.ForJarFile.of(agentJarFile)), + AgentBuilder.LocationStrategy.ForClassLoader.WEAK, + new AgentBuilder.LocationStrategy.Simple(new RootPackageCustomLocator("java.", ClassFileLocator.ForClassLoader.ofBootLoader())) + ); } catch (IOException e) { logger.warn("Failed to add ClassFileLocator for the agent jar. Some instrumentations may not work", e); } diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/configuration/StartupInfo.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/configuration/StartupInfo.java index d57cde7078..eea794cefc 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/configuration/StartupInfo.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/configuration/StartupInfo.java @@ -58,12 +58,15 @@ public StartupInfo() { } private static String getJvmAndOsVersionString() { - return "Java " + System.getProperty("java.version") + " (" + System.getProperty("java.vendor") + ") " + + return "Java " + System.getProperty("java.version") + + " Runtime version: "+ System.getProperty("java.runtime.version") + + " VM version: "+ System.getProperty("java.vm.version") + + " (" + System.getProperty("java.vendor") + ") " + System.getProperty("os.name") + " " + System.getProperty("os.version"); } @Override - public void start(ElasticApmTracer tracer) { + public void init(ElasticApmTracer tracer) { ConfigurationRegistry configurationRegistry = tracer.getConfigurationRegistry(); logConfiguration(configurationRegistry, logger); } diff --git a/apm-agent-core/src/test/java/co/elastic/apm/agent/bci/AgentMainTest.java b/apm-agent-core/src/test/java/co/elastic/apm/agent/bci/AgentMainTest.java index 6c97a1691d..36c3a92e63 100644 --- a/apm-agent-core/src/test/java/co/elastic/apm/agent/bci/AgentMainTest.java +++ b/apm-agent-core/src/test/java/co/elastic/apm/agent/bci/AgentMainTest.java @@ -140,9 +140,15 @@ void shouldBeSupportedInCaseOfParsingError() { )); } + @Test + void testIbmJava8SupportedAfterBuild2_8() { + assertThat(AgentMain.isJavaVersionSupported("1.8.0", "IBM J9 VM", "2.8")).isFalse(); + assertThat(AgentMain.isJavaVersionSupported("1.8.0", "IBM J9 VM", "2.9")).isTrue(); + } + private static void checkSupported(String vmName, Stream versions) { versions.forEach((v) -> { - boolean supported = AgentMain.isJavaVersionSupported(v, vmName); + boolean supported = AgentMain.isJavaVersionSupported(v, vmName, null); assertThat(supported) .describedAs("java.version = '%s' java.vm.name = '%s' should be supported", v, vmName) .isTrue(); @@ -151,7 +157,7 @@ private static void checkSupported(String vmName, Stream versions) { private static void checkNotSupported(String vmName, Stream versions) { versions.forEach((v) -> { - boolean supported = AgentMain.isJavaVersionSupported(v, vmName); + boolean supported = AgentMain.isJavaVersionSupported(v, vmName, null); assertThat(supported) .describedAs("java.version = '%s' java.vm.name = '%s' should not be supported", v, vmName) .isFalse(); diff --git a/apm-agent-plugins/apm-grpc/apm-grpc-test-latest/src/test/java/co/elastic/apm/agent/grpc/latest/testapp/generated/HelloGrpc.java b/apm-agent-plugins/apm-grpc/apm-grpc-test-latest/src/test/java/co/elastic/apm/agent/grpc/latest/testapp/generated/HelloGrpc.java index 682a0c4b7b..1a119e9f6b 100644 --- a/apm-agent-plugins/apm-grpc/apm-grpc-test-latest/src/test/java/co/elastic/apm/agent/grpc/latest/testapp/generated/HelloGrpc.java +++ b/apm-agent-plugins/apm-grpc/apm-grpc-test-latest/src/test/java/co/elastic/apm/agent/grpc/latest/testapp/generated/HelloGrpc.java @@ -42,7 +42,7 @@ /** */ @javax.annotation.Generated( - value = "by gRPC proto compiler (version 1.30.0)", + value = "by gRPC proto compiler (version 1.30.1)", comments = "Source: rpc.proto") public final class HelloGrpc { diff --git a/docs/supported-technologies.asciidoc b/docs/supported-technologies.asciidoc index 62250457b5..772c94ecc0 100644 --- a/docs/supported-technologies.asciidoc +++ b/docs/supported-technologies.asciidoc @@ -52,7 +52,7 @@ the agent does not capture transactions. |`--module-path` has not been tested yet |IBM J9 VM -|8 +|8 service refresh 5+ | |=== diff --git a/integration-tests/application-server-integration-tests/src/test/java/co/elastic/apm/servlet/WebSphereIT.java b/integration-tests/application-server-integration-tests/src/test/java/co/elastic/apm/servlet/WebSphereIT.java index 38626b259d..98dfd023f2 100644 --- a/integration-tests/application-server-integration-tests/src/test/java/co/elastic/apm/servlet/WebSphereIT.java +++ b/integration-tests/application-server-integration-tests/src/test/java/co/elastic/apm/servlet/WebSphereIT.java @@ -55,7 +55,12 @@ public WebSphereIT(final String version) { @Parameterized.Parameters(name = "WebSphere {0}") public static Iterable data() { - return Arrays.asList(new Object[][]{{"8.5.5"}, {"webProfile7"}}); + return Arrays.asList(new Object[][]{ + {"webProfile6"}, + {"webProfile7"}, + {"webProfile8"}, + {"latest"} + }); } @Override diff --git a/integration-tests/cdi-app/cdi-app-dependent/src/main/webapp/WEB-INF/web.xml b/integration-tests/cdi-app/cdi-app-dependent/src/main/webapp/WEB-INF/web.xml index 4f86ee3fe6..ef8db8d4bb 100644 --- a/integration-tests/cdi-app/cdi-app-dependent/src/main/webapp/WEB-INF/web.xml +++ b/integration-tests/cdi-app/cdi-app-dependent/src/main/webapp/WEB-INF/web.xml @@ -1,8 +1,8 @@ - + xsi:schemaLocation="http://java.sun.com/xml/ns/javaee + http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" + version="3.0"> CDI App diff --git a/integration-tests/jsf-app/jsf-app-dependent/src/main/webapp/WEB-INF/web.xml b/integration-tests/jsf-app/jsf-app-dependent/src/main/webapp/WEB-INF/web.xml index c5e7c7381b..e1d3e55324 100644 --- a/integration-tests/jsf-app/jsf-app-dependent/src/main/webapp/WEB-INF/web.xml +++ b/integration-tests/jsf-app/jsf-app-dependent/src/main/webapp/WEB-INF/web.xml @@ -1,5 +1,9 @@ - + javax.faces.PROJECT_STAGE Development diff --git a/integration-tests/simple-webapp/src/main/webapp/WEB-INF/web.xml b/integration-tests/simple-webapp/src/main/webapp/WEB-INF/web.xml index dc1b98c0c8..b4e1642329 100644 --- a/integration-tests/simple-webapp/src/main/webapp/WEB-INF/web.xml +++ b/integration-tests/simple-webapp/src/main/webapp/WEB-INF/web.xml @@ -1,8 +1,8 @@ - + xsi:schemaLocation="http://java.sun.com/xml/ns/javaee + http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" + version="3.0"> Simple Web App diff --git a/integration-tests/soap-test/src/main/webapp/WEB-INF/web.xml b/integration-tests/soap-test/src/main/webapp/WEB-INF/web.xml index d4aab707ae..2989f68575 100644 --- a/integration-tests/soap-test/src/main/webapp/WEB-INF/web.xml +++ b/integration-tests/soap-test/src/main/webapp/WEB-INF/web.xml @@ -1,6 +1,9 @@ - + SoapClientServlet From c55474b6c1e9f894fbecb711cfbbe8667ac75f7a Mon Sep 17 00:00:00 2001 From: Felix Barnsteiner Date: Mon, 22 Jun 2020 17:00:18 +0200 Subject: [PATCH 29/35] Fix Spring Boot tests --- integration-tests/spring-boot-2/spring-boot-2-base/pom.xml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/integration-tests/spring-boot-2/spring-boot-2-base/pom.xml b/integration-tests/spring-boot-2/spring-boot-2-base/pom.xml index e9aa57a6da..75e00756d9 100644 --- a/integration-tests/spring-boot-2/spring-boot-2-base/pom.xml +++ b/integration-tests/spring-boot-2/spring-boot-2-base/pom.xml @@ -59,11 +59,6 @@ spring-boot-starter-test ${version.spring-boot} - - net.bytebuddy - byte-buddy - ${version.byte-buddy} - From a0168762b9651de70f7e66155583a5b84d611548 Mon Sep 17 00:00:00 2001 From: Felix Barnsteiner Date: Thu, 25 Jun 2020 18:12:06 +0200 Subject: [PATCH 30/35] Add bytecode examples --- .../elastic/apm/agent/bci/IndyBootstrap.java | 32 ++++++++++++++++++- .../bci/bytebuddy/postprocessor/AssignTo.java | 27 ++++++++++++++++ 2 files changed, 58 insertions(+), 1 deletion(-) diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/IndyBootstrap.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/IndyBootstrap.java index 4f6ce04170..3fec20d115 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/IndyBootstrap.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/IndyBootstrap.java @@ -217,12 +217,42 @@ private static Class initIndyBootstrap() throws Exception { *

* Exceptions and {@code null} return values are handled by {@code java.lang.IndyBootstrapDispatcher#bootstrap}. *

+ * This is how a bootstrap method looks like in the class file: + *
+     * BootstrapMethods:
+     *   1: #1060 REF_invokeStatic java/lang/IndyBootstrapDispatcher.bootstrap:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
+     *     Method arguments:
+     *       #1049 co.elastic.apm.agent.bci.InstrumentationTest$CommonsLangInstrumentation
+     *       #1050 0
+     *       #12 org/apache/commons/lang3/StringUtils
+     *       #1072 isNotEmpty
+     *       #1075 REF_invokeStatic org/apache/commons/lang3/StringUtils.isNotEmpty:(Ljava/lang/CharSequence;)Z
+     * 
+ * + * And this is how a invokedynamic instruction looks like inside methods, + * referencing above bootstrap method + *
+     *     invokedynamic #1076,  0           // InvokeDynamic #1:onEnter:()V
+     * 
+ * + * @param lookup A {@code java.lang.invoke.MethodHandles.Lookup} representing the instrumented method. + * @param adviceMethodName A {@link String} representing the advice method name. + * @param adviceMethodType A {@link java.lang.invoke.MethodType} representing the arguments and return type of the advice method. + * @param args Additional arguments that are provided by Byte Buddy: + *
    + *
  • A {@link String} of the binary target class name.
  • + *
  • A {@link int} with value {@code 0} for an enter advice and {code 1} for an exist advice.
  • + *
  • A {@link Class} representing the class implementing the instrumented method.
  • + *
  • A {@link String} with the name of the instrumented method.
  • + *
  • A {@link java.lang.invoke.MethodHandle} representing the instrumented method unless the target is the type's static initializer.
  • + *
+ * @return a {@link ConstantCallSite} that is the target of the invokedynamic */ @Nullable public static ConstantCallSite bootstrap(MethodHandles.Lookup lookup, String adviceMethodName, MethodType adviceMethodType, - Object... args) throws Exception { + Object... args) { try { String adviceClassName = (String) args[0]; int enter = (Integer) args[1]; diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignTo.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignTo.java index 8fabd950cd..e31b70584b 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignTo.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/postprocessor/AssignTo.java @@ -59,6 +59,33 @@ * Works by returning an {@code Object[]} from the advice method. * * + * + * Taking an argument assignment as an example, the resulting code looks like this when decompiled: + *
+ *     public String assignToArgument(String arg) {
+ *         String var10000;
+ *         try {
+ *             // result of inline = false
+ *             var10000 = co.elastic.apm.agent.bci.InstrumentationTest.AssignToArgumentInstrumentation.onEnter(s);
+ *         } catch (Throwable var3) {
+ *             // result of suppress = Throwable.class
+ *             var3.printStackTrace();
+ *             var10000 = null;
+ *         }
+ *
+ *         // this is the result of the @AssignTo.Argument(0) post processor
+ *         // it's just a piece of code that's executed after the advice that has access to the return value of the advice (var10000)
+ *         // this assignment takes care of type conversions, according to {@link Argument#typing()}
+ *         String var2 = var10000;
+ *         // the null check avoids that we override the argument with null in case of an suppressed exception within the advice
+ *         if (var2 != null) {
+ *             // the actual assignment to the argument
+ *             arg = var2;
+ *         }
+ *
+ *         return arg;
+ *     }
+ * 
*/ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) From 076f116112fb1d97a62878c877cd9693160c199f Mon Sep 17 00:00:00 2001 From: Felix Barnsteiner Date: Thu, 25 Jun 2020 18:26:58 +0200 Subject: [PATCH 31/35] Apply suggestions from code review Co-authored-by: eyalkoren <41850454+eyalkoren@users.noreply.github.com> --- .../elastic/apm/agent/bci/ElasticApmInstrumentation.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/ElasticApmInstrumentation.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/ElasticApmInstrumentation.java index e09684e82e..8e53f38f44 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/ElasticApmInstrumentation.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/ElasticApmInstrumentation.java @@ -198,7 +198,7 @@ public void onTypeMatch(TypeDescription typeDescription, ClassLoader classLoader /** * When this method returns {@code true} the whole package (starting at the {@linkplain #getAdviceClass() advice's} package) * will be loaded from a plugin class loader that has both the agent class loader and the class loader of the class this instruments as - * the parent. + * parents. *

* This instructs Byte Buddy to dispatch to the advice methods via an {@code INVOKEDYNAMIC} instruction. * Upon first invocation of an instrumented method, @@ -215,11 +215,11 @@ public void onTypeMatch(TypeDescription typeDescription, ClassLoader classLoader * use {@link co.elastic.apm.agent.bci.bytebuddy.postprocessor.AssignTo} and friends. * *

  • - * Both the return type and the arguments of advice methods must no contain types from the agent. + * Both the return type and the arguments of advice methods must not contain types from the agent. * If you'd like to return a {@link Span} from an advice, for example, return an {@link Object} instead. * When using an {@link net.bytebuddy.asm.Advice.Enter} argument on the * {@linkplain net.bytebuddy.asm.Advice.OnMethodExit exit advice}, - * that argument als has to be of type {@link Object} and you have to cast in within the method body. + * that argument also has to be of type {@link Object} and you have to cast it within the method body. * The reason is that the return value will become a local variable in the instrumented method. * Due to OSGi, those methods may not have access to agent types. * Another case is when the instrumented class is inside the bootstrap classloader. @@ -227,7 +227,7 @@ public void onTypeMatch(TypeDescription typeDescription, ClassLoader classLoader *
  • * When an advice instruments classes in multiple class loaders, the plugin classes will be loaded form multiple class loaders. * In order to still share state across those plugin class loaders, use {@link co.elastic.apm.agent.util.GlobalVariables} or {@link GlobalState}. - * That's necessary as a static variables are scoped to the class loader they are defined in. + * That's necessary as static variables are scoped to the class loader they are defined in. *
  • *
  • * Don't use {@link ThreadLocal}s as it can lead to class loader leaks. From eb12fb19fb0977814928fb2f2bedd1492610f565 Mon Sep 17 00:00:00 2001 From: Felix Barnsteiner Date: Mon, 29 Jun 2020 09:53:59 +0200 Subject: [PATCH 32/35] Implement suggestions from review --- .../apm/agent/bci/ElasticApmAgent.java | 12 ++++ .../agent/bci/ElasticApmInstrumentation.java | 3 +- .../apm/agent/bci/HelperClassManager.java | 7 +- .../elastic/apm/agent/bci/IndyBootstrap.java | 3 +- ...hreadLocal.java => GlobalThreadLocal.java} | 23 ++++--- .../apm/agent/util/PackageScanner.java | 11 +-- .../apm/agent/bci/InstrumentationTest.java | 65 ++++++++++++++++++ ...asticsearchClientAsyncInstrumentation.java | 4 +- ...asticsearchClientAsyncInstrumentation.java | 4 +- .../okhttp/OkHttp3ClientInstrumentation.java | 4 +- .../okhttp/OkHttpClientInstrumentation.java | 4 +- .../apm/agent/servlet/ServletApiAdvice.java | 4 +- ...mentationTest-original.1593101617596.class | Bin 16761 -> 0 bytes ...ci.InstrumentationTest.1593101617596.class | Bin 16868 -> 0 bytes ...158$auxiliary$9KIyCM3k.1593101616146.class | Bin 842 -> 0 bytes ...158$auxiliary$ALCg4um1.1593101616156.class | Bin 867 -> 0 bytes ...158$auxiliary$AOTobfsn.1593101616185.class | Bin 813 -> 0 bytes ...158$auxiliary$BHFfOhHy.1593101616159.class | Bin 817 -> 0 bytes ...158$auxiliary$D3AsD9bA.1593101616101.class | Bin 764 -> 0 bytes ...158$auxiliary$DAdwndeX.1593101616178.class | Bin 861 -> 0 bytes ...158$auxiliary$DGg8cD2o.1593101616115.class | Bin 841 -> 0 bytes ...158$auxiliary$DKEklDMw.1593101616191.class | Bin 824 -> 0 bytes ...158$auxiliary$DiTUnpJy.1593101616104.class | Bin 803 -> 0 bytes ...158$auxiliary$HhndzgD5.1593101616166.class | Bin 855 -> 0 bytes ...158$auxiliary$J2CcklM1.1593101616151.class | Bin 858 -> 0 bytes ...158$auxiliary$KLOSCKbi.1593101616148.class | Bin 852 -> 0 bytes ...158$auxiliary$Lusl2RCn.1593101616097.class | Bin 790 -> 0 bytes ...158$auxiliary$NNSe8iYz.1593101616180.class | Bin 869 -> 0 bytes ...158$auxiliary$ObT25b20.1593101616168.class | Bin 825 -> 0 bytes ...158$auxiliary$OmfutFYx.1593101616125.class | Bin 805 -> 0 bytes ...158$auxiliary$Pk31czbr.1593101616136.class | Bin 858 -> 0 bytes ...158$auxiliary$Q8nB82pi.1593101616195.class | Bin 848 -> 0 bytes ...158$auxiliary$R7X3izBv.1593101616162.class | Bin 795 -> 0 bytes ...158$auxiliary$RMMg2hhP.1593101616171.class | Bin 871 -> 0 bytes ...158$auxiliary$Vd2B0qRj.1593101616123.class | Bin 836 -> 0 bytes ...158$auxiliary$XdkQZ0Zs.1593101616129.class | Bin 799 -> 0 bytes ...158$auxiliary$aHu8a2kE.1593101616134.class | Bin 796 -> 0 bytes ...158$auxiliary$cUr8u3Cb.1593101616173.class | Bin 794 -> 0 bytes ...158$auxiliary$eHiGqA6O.1593101616182.class | Bin 858 -> 0 bytes ...158$auxiliary$eyL6Gf0w.1593101616143.class | Bin 799 -> 0 bytes ...158$auxiliary$iy7FDeW1.1593101616131.class | Bin 793 -> 0 bytes ...158$auxiliary$k6gDMEVF.1593101616106.class | Bin 796 -> 0 bytes ...158$auxiliary$l6ltS3z8.1593101616188.class | Bin 817 -> 0 bytes ...158$auxiliary$nrbzqUgq.1593101616120.class | Bin 844 -> 0 bytes ...158$auxiliary$nwGKpHW3.1593101616197.class | Bin 845 -> 0 bytes ...158$auxiliary$oQrfXJEV.1593101616127.class | Bin 800 -> 0 bytes ...158$auxiliary$oqx9Tg8I.1593101616153.class | Bin 867 -> 0 bytes ...158$auxiliary$rvFiPylb.1593101616139.class | Bin 801 -> 0 bytes ...158$auxiliary$vZ1jiPer.1593101616113.class | Bin 799 -> 0 bytes ...158$auxiliary$wFSmU0Ph.1593101616110.class | Bin 829 -> 0 bytes ...158$auxiliary$wrKmhL8U.1593101616176.class | Bin 858 -> 0 bytes ...$MockitoMock$450365158.1593101616095.class | Bin 15607 -> 0 bytes ...386$auxiliary$7zXGjbP9.1593101616358.class | Bin 892 -> 0 bytes ...386$auxiliary$NsWtl48K.1593101616353.class | Bin 788 -> 0 bytes ...386$auxiliary$SQ1Np2ea.1593101616362.class | Bin 885 -> 0 bytes ...386$auxiliary$ZBMbCmAS.1593101616360.class | Bin 826 -> 0 bytes ...386$auxiliary$t8RTyiFM.1593101616356.class | Bin 827 -> 0 bytes ...386$auxiliary$uqYB0hX2.1593101616364.class | Bin 895 -> 0 bytes ...386$auxiliary$zSH2u0qa.1593101616352.class | Bin 814 -> 0 bytes ...MockitoMock$2120571386.1593101616351.class | Bin 4734 -> 0 bytes ...970$auxiliary$0B2hXgmq.1593101616460.class | Bin 1226 -> 0 bytes ...970$auxiliary$240UPexK.1593101616500.class | Bin 785 -> 0 bytes ...970$auxiliary$2Jg1XhI6.1593101616525.class | Bin 823 -> 0 bytes ...970$auxiliary$AAA0s3cg.1593101616496.class | Bin 778 -> 0 bytes ...970$auxiliary$BsIjxbKl.1593101616524.class | Bin 890 -> 0 bytes ...970$auxiliary$DQzSzmpl.1593101616443.class | Bin 1138 -> 0 bytes ...970$auxiliary$DgFwG7Hk.1593101616444.class | Bin 910 -> 0 bytes ...970$auxiliary$F1y9MwK0.1593101616469.class | Bin 1139 -> 0 bytes ...970$auxiliary$FWInoz7F.1593101616518.class | Bin 973 -> 0 bytes ...970$auxiliary$G6i7FVij.1593101616522.class | Bin 771 -> 0 bytes ...970$auxiliary$G9Soc3Un.1593101616509.class | Bin 805 -> 0 bytes ...970$auxiliary$GNAHCepU.1593101616479.class | Bin 1213 -> 0 bytes ...970$auxiliary$Gr2vHXQC.1593101616481.class | Bin 802 -> 0 bytes ...970$auxiliary$Is0bPu72.1593101616531.class | Bin 788 -> 0 bytes ...970$auxiliary$MDfVBZ3E.1593101616527.class | Bin 724 -> 0 bytes ...970$auxiliary$MXBuDuLH.1593101616511.class | Bin 859 -> 0 bytes ...970$auxiliary$Mr3Ng1rI.1593101616441.class | Bin 724 -> 0 bytes ...970$auxiliary$NcHVc13I.1593101616484.class | Bin 910 -> 0 bytes ...970$auxiliary$OKGB6GNl.1593101616448.class | Bin 1455 -> 0 bytes ...970$auxiliary$PcPJF5oi.1593101616494.class | Bin 773 -> 0 bytes ...970$auxiliary$PzweZZ4J.1593101616473.class | Bin 1028 -> 0 bytes ...970$auxiliary$SDJCxIlV.1593101616492.class | Bin 916 -> 0 bytes ...970$auxiliary$T91ZgJXd.1593101616497.class | Bin 795 -> 0 bytes ...970$auxiliary$UeMGnoGm.1593101616501.class | Bin 929 -> 0 bytes ...970$auxiliary$XOlg3lAd.1593101616521.class | Bin 787 -> 0 bytes ...970$auxiliary$XoYwdXNW.1593101616475.class | Bin 1129 -> 0 bytes ...970$auxiliary$YNfVa5qr.1593101616504.class | Bin 735 -> 0 bytes ...970$auxiliary$ZGNUVBit.1593101616463.class | Bin 1461 -> 0 bytes ...970$auxiliary$bOUDRcqT.1593101616503.class | Bin 762 -> 0 bytes ...970$auxiliary$bTp1obb7.1593101616529.class | Bin 725 -> 0 bytes ...970$auxiliary$coLFJ9Zn.1593101616457.class | Bin 1232 -> 0 bytes ...970$auxiliary$dfA2UpPS.1593101616516.class | Bin 786 -> 0 bytes ...970$auxiliary$eMARe4Lh.1593101616476.class | Bin 974 -> 0 bytes ...970$auxiliary$eNVZsKqr.1593101616490.class | Bin 910 -> 0 bytes ...970$auxiliary$hiCh2uJq.1593101616488.class | Bin 931 -> 0 bytes ...970$auxiliary$inZx0SYn.1593101616467.class | Bin 793 -> 0 bytes ...970$auxiliary$l6vxjq2r.1593101616519.class | Bin 744 -> 0 bytes ...970$auxiliary$lFVZWOU4.1593101616486.class | Bin 917 -> 0 bytes ...970$auxiliary$nYaBoRKW.1593101616483.class | Bin 938 -> 0 bytes ...970$auxiliary$pAZ1ZMCC.1593101616513.class | Bin 935 -> 0 bytes ...970$auxiliary$qL94bV7z.1593101616439.class | Bin 750 -> 0 bytes ...970$auxiliary$qbo7evRl.1593101616507.class | Bin 724 -> 0 bytes ...970$auxiliary$tlsZFOzv.1593101616506.class | Bin 735 -> 0 bytes ...970$auxiliary$tnnsEeCo.1593101616514.class | Bin 937 -> 0 bytes ...970$auxiliary$u8urv8mc.1593101616528.class | Bin 723 -> 0 bytes ...970$auxiliary$wQexNdSn.1593101616465.class | Bin 790 -> 0 bytes ...970$auxiliary$yDUH5VxD.1593101616471.class | Bin 1189 -> 0 bytes ...$MockitoMock$452245970.1593101616438.class | Bin 25539 -> 0 bytes ...442$auxiliary$GwsRwzTe.1593101616010.class | Bin 766 -> 0 bytes ...442$auxiliary$OscL2qTK.1593101616019.class | Bin 740 -> 0 bytes ...442$auxiliary$X025iH8m.1593101616023.class | Bin 779 -> 0 bytes ...442$auxiliary$ZWSBqb7R.1593101616026.class | Bin 810 -> 0 bytes ...MockitoMock$1166487442.1593101615981.class | Bin 3485 -> 0 bytes ...726$auxiliary$8Mb9HwGZ.1593101616379.class | Bin 938 -> 0 bytes ...726$auxiliary$APBxdT8h.1593101616385.class | Bin 929 -> 0 bytes ...726$auxiliary$BAX1I2Nz.1593101616382.class | Bin 922 -> 0 bytes ...726$auxiliary$E9ZzXDgv.1593101616373.class | Bin 858 -> 0 bytes ...726$auxiliary$RNW7KT5w.1593101616381.class | Bin 922 -> 0 bytes ...726$auxiliary$RwZzUwxo.1593101616383.class | Bin 933 -> 0 bytes ...726$auxiliary$W0PgUqhS.1593101616374.class | Bin 832 -> 0 bytes ...726$auxiliary$hn0y2lsw.1593101616377.class | Bin 927 -> 0 bytes ...726$auxiliary$oRvI9CJq.1593101616376.class | Bin 871 -> 0 bytes ...726$auxiliary$onUvPA51.1593101616386.class | Bin 929 -> 0 bytes ...MockitoMock$1307674726.1593101616372.class | Bin 5800 -> 0 bytes ...899$auxiliary$3w0OHfSP.1593101616332.class | Bin 801 -> 0 bytes ...899$auxiliary$8UVTuVgW.1593101616331.class | Bin 811 -> 0 bytes ...899$auxiliary$J5m1M3Gj.1593101616336.class | Bin 859 -> 0 bytes ...899$auxiliary$SusFhZBs.1593101616338.class | Bin 800 -> 0 bytes ...899$auxiliary$X21vquhk.1593101616329.class | Bin 772 -> 0 bytes ...899$auxiliary$vAfycOnq.1593101616334.class | Bin 807 -> 0 bytes ...899$auxiliary$wMh2jraR.1593101616340.class | Bin 810 -> 0 bytes ...899$auxiliary$xryI3jE3.1593101616327.class | Bin 798 -> 0 bytes ...$MockitoMock$880733899.1593101616325.class | Bin 4787 -> 0 bytes ...855$auxiliary$2KyRX7o0.1593101616278.class | Bin 800 -> 0 bytes ...855$auxiliary$5swnRP6k.1593101616280.class | Bin 839 -> 0 bytes ...855$auxiliary$BmSrhKdb.1593101616284.class | Bin 844 -> 0 bytes ...855$auxiliary$Tx0jQIZ4.1593101616275.class | Bin 826 -> 0 bytes ...855$auxiliary$X6N173wU.1593101616282.class | Bin 892 -> 0 bytes ...855$auxiliary$jIw4bUWQ.1593101616286.class | Bin 890 -> 0 bytes ...MockitoMock$1707322855.1593101616274.class | Bin 4287 -> 0 bytes ...129$auxiliary$0zVk9feM.1593101616311.class | Bin 851 -> 0 bytes ...129$auxiliary$QWqZNH2R.1593101616308.class | Bin 795 -> 0 bytes ...129$auxiliary$g4c944Dn.1593101616315.class | Bin 812 -> 0 bytes ...129$auxiliary$jgjMka3t.1593101616313.class | Bin 843 -> 0 bytes ...129$auxiliary$pTrzOMjn.1593101616310.class | Bin 784 -> 0 bytes ...129$auxiliary$tiKQ5Tkj.1593101616306.class | Bin 756 -> 0 bytes ...129$auxiliary$wG2reRR1.1593101616303.class | Bin 782 -> 0 bytes ...MockitoMock$2051601129.1593101616302.class | Bin 4413 -> 0 bytes ...840$auxiliary$Cc88yyoF.1593101616231.class | Bin 791 -> 0 bytes ...840$auxiliary$Ciailu7N.1593101616247.class | Bin 827 -> 0 bytes ...840$auxiliary$FQIF7r35.1593101616244.class | Bin 781 -> 0 bytes ...840$auxiliary$WscYjDAZ.1593101616241.class | Bin 779 -> 0 bytes ...840$auxiliary$aPkuzhc7.1593101616239.class | Bin 784 -> 0 bytes ...840$auxiliary$e5JFRF6z.1593101616261.class | Bin 823 -> 0 bytes ...840$auxiliary$f9wdS49a.1593101616257.class | Bin 828 -> 0 bytes ...840$auxiliary$fml0gMeJ.1593101616227.class | Bin 778 -> 0 bytes ...840$auxiliary$oRNIUTuB.1593101616255.class | Bin 849 -> 0 bytes ...840$auxiliary$qvojxnDz.1593101616249.class | Bin 842 -> 0 bytes ...840$auxiliary$rjkBB8hS.1593101616263.class | Bin 785 -> 0 bytes ...840$auxiliary$t827eD2N.1593101616229.class | Bin 752 -> 0 bytes ...840$auxiliary$tC0jrLOr.1593101616259.class | Bin 835 -> 0 bytes ...840$auxiliary$wXFwQ9X3.1593101616234.class | Bin 839 -> 0 bytes ...840$auxiliary$yAFPWn8m.1593101616236.class | Bin 838 -> 0 bytes ...840$auxiliary$zpZctcuy.1593101616252.class | Bin 845 -> 0 bytes ...$MockitoMock$261936840.1593101616225.class | Bin 7841 -> 0 bytes ...BuddyAccessor$fvlmDHgd.1593101615727.class | Bin 1308 -> 0 bytes ...yteBuddy$ModuleSupport.1593101617400.class | Bin 2664 -> 0 bytes 167 files changed, 107 insertions(+), 37 deletions(-) rename apm-agent-core/src/main/java/co/elastic/apm/agent/threadlocal/{RemoveOnGetThreadLocal.java => GlobalThreadLocal.java} (65%) delete mode 100644 dump/co.elastic.apm.agent.bci.InstrumentationTest-original.1593101617596.class delete mode 100644 dump/co.elastic.apm.agent.bci.InstrumentationTest.1593101617596.class delete mode 100644 dump/co.elastic.apm.agent.configuration.CoreConfiguration$MockitoMock$450365158$auxiliary$9KIyCM3k.1593101616146.class delete mode 100644 dump/co.elastic.apm.agent.configuration.CoreConfiguration$MockitoMock$450365158$auxiliary$ALCg4um1.1593101616156.class delete mode 100644 dump/co.elastic.apm.agent.configuration.CoreConfiguration$MockitoMock$450365158$auxiliary$AOTobfsn.1593101616185.class delete mode 100644 dump/co.elastic.apm.agent.configuration.CoreConfiguration$MockitoMock$450365158$auxiliary$BHFfOhHy.1593101616159.class delete mode 100644 dump/co.elastic.apm.agent.configuration.CoreConfiguration$MockitoMock$450365158$auxiliary$D3AsD9bA.1593101616101.class delete mode 100644 dump/co.elastic.apm.agent.configuration.CoreConfiguration$MockitoMock$450365158$auxiliary$DAdwndeX.1593101616178.class delete mode 100644 dump/co.elastic.apm.agent.configuration.CoreConfiguration$MockitoMock$450365158$auxiliary$DGg8cD2o.1593101616115.class delete mode 100644 dump/co.elastic.apm.agent.configuration.CoreConfiguration$MockitoMock$450365158$auxiliary$DKEklDMw.1593101616191.class delete mode 100644 dump/co.elastic.apm.agent.configuration.CoreConfiguration$MockitoMock$450365158$auxiliary$DiTUnpJy.1593101616104.class delete mode 100644 dump/co.elastic.apm.agent.configuration.CoreConfiguration$MockitoMock$450365158$auxiliary$HhndzgD5.1593101616166.class delete mode 100644 dump/co.elastic.apm.agent.configuration.CoreConfiguration$MockitoMock$450365158$auxiliary$J2CcklM1.1593101616151.class delete mode 100644 dump/co.elastic.apm.agent.configuration.CoreConfiguration$MockitoMock$450365158$auxiliary$KLOSCKbi.1593101616148.class delete mode 100644 dump/co.elastic.apm.agent.configuration.CoreConfiguration$MockitoMock$450365158$auxiliary$Lusl2RCn.1593101616097.class delete mode 100644 dump/co.elastic.apm.agent.configuration.CoreConfiguration$MockitoMock$450365158$auxiliary$NNSe8iYz.1593101616180.class delete mode 100644 dump/co.elastic.apm.agent.configuration.CoreConfiguration$MockitoMock$450365158$auxiliary$ObT25b20.1593101616168.class delete mode 100644 dump/co.elastic.apm.agent.configuration.CoreConfiguration$MockitoMock$450365158$auxiliary$OmfutFYx.1593101616125.class delete mode 100644 dump/co.elastic.apm.agent.configuration.CoreConfiguration$MockitoMock$450365158$auxiliary$Pk31czbr.1593101616136.class delete mode 100644 dump/co.elastic.apm.agent.configuration.CoreConfiguration$MockitoMock$450365158$auxiliary$Q8nB82pi.1593101616195.class delete mode 100644 dump/co.elastic.apm.agent.configuration.CoreConfiguration$MockitoMock$450365158$auxiliary$R7X3izBv.1593101616162.class delete mode 100644 dump/co.elastic.apm.agent.configuration.CoreConfiguration$MockitoMock$450365158$auxiliary$RMMg2hhP.1593101616171.class delete mode 100644 dump/co.elastic.apm.agent.configuration.CoreConfiguration$MockitoMock$450365158$auxiliary$Vd2B0qRj.1593101616123.class delete mode 100644 dump/co.elastic.apm.agent.configuration.CoreConfiguration$MockitoMock$450365158$auxiliary$XdkQZ0Zs.1593101616129.class delete mode 100644 dump/co.elastic.apm.agent.configuration.CoreConfiguration$MockitoMock$450365158$auxiliary$aHu8a2kE.1593101616134.class delete mode 100644 dump/co.elastic.apm.agent.configuration.CoreConfiguration$MockitoMock$450365158$auxiliary$cUr8u3Cb.1593101616173.class delete mode 100644 dump/co.elastic.apm.agent.configuration.CoreConfiguration$MockitoMock$450365158$auxiliary$eHiGqA6O.1593101616182.class delete mode 100644 dump/co.elastic.apm.agent.configuration.CoreConfiguration$MockitoMock$450365158$auxiliary$eyL6Gf0w.1593101616143.class delete mode 100644 dump/co.elastic.apm.agent.configuration.CoreConfiguration$MockitoMock$450365158$auxiliary$iy7FDeW1.1593101616131.class delete mode 100644 dump/co.elastic.apm.agent.configuration.CoreConfiguration$MockitoMock$450365158$auxiliary$k6gDMEVF.1593101616106.class delete mode 100644 dump/co.elastic.apm.agent.configuration.CoreConfiguration$MockitoMock$450365158$auxiliary$l6ltS3z8.1593101616188.class delete mode 100644 dump/co.elastic.apm.agent.configuration.CoreConfiguration$MockitoMock$450365158$auxiliary$nrbzqUgq.1593101616120.class delete mode 100644 dump/co.elastic.apm.agent.configuration.CoreConfiguration$MockitoMock$450365158$auxiliary$nwGKpHW3.1593101616197.class delete mode 100644 dump/co.elastic.apm.agent.configuration.CoreConfiguration$MockitoMock$450365158$auxiliary$oQrfXJEV.1593101616127.class delete mode 100644 dump/co.elastic.apm.agent.configuration.CoreConfiguration$MockitoMock$450365158$auxiliary$oqx9Tg8I.1593101616153.class delete mode 100644 dump/co.elastic.apm.agent.configuration.CoreConfiguration$MockitoMock$450365158$auxiliary$rvFiPylb.1593101616139.class delete mode 100644 dump/co.elastic.apm.agent.configuration.CoreConfiguration$MockitoMock$450365158$auxiliary$vZ1jiPer.1593101616113.class delete mode 100644 dump/co.elastic.apm.agent.configuration.CoreConfiguration$MockitoMock$450365158$auxiliary$wFSmU0Ph.1593101616110.class delete mode 100644 dump/co.elastic.apm.agent.configuration.CoreConfiguration$MockitoMock$450365158$auxiliary$wrKmhL8U.1593101616176.class delete mode 100644 dump/co.elastic.apm.agent.configuration.CoreConfiguration$MockitoMock$450365158.1593101616095.class delete mode 100644 dump/co.elastic.apm.agent.configuration.MessagingConfiguration$MockitoMock$2120571386$auxiliary$7zXGjbP9.1593101616358.class delete mode 100644 dump/co.elastic.apm.agent.configuration.MessagingConfiguration$MockitoMock$2120571386$auxiliary$NsWtl48K.1593101616353.class delete mode 100644 dump/co.elastic.apm.agent.configuration.MessagingConfiguration$MockitoMock$2120571386$auxiliary$SQ1Np2ea.1593101616362.class delete mode 100644 dump/co.elastic.apm.agent.configuration.MessagingConfiguration$MockitoMock$2120571386$auxiliary$ZBMbCmAS.1593101616360.class delete mode 100644 dump/co.elastic.apm.agent.configuration.MessagingConfiguration$MockitoMock$2120571386$auxiliary$t8RTyiFM.1593101616356.class delete mode 100644 dump/co.elastic.apm.agent.configuration.MessagingConfiguration$MockitoMock$2120571386$auxiliary$uqYB0hX2.1593101616364.class delete mode 100644 dump/co.elastic.apm.agent.configuration.MessagingConfiguration$MockitoMock$2120571386$auxiliary$zSH2u0qa.1593101616352.class delete mode 100644 dump/co.elastic.apm.agent.configuration.MessagingConfiguration$MockitoMock$2120571386.1593101616351.class delete mode 100644 dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$0B2hXgmq.1593101616460.class delete mode 100644 dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$240UPexK.1593101616500.class delete mode 100644 dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$2Jg1XhI6.1593101616525.class delete mode 100644 dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$AAA0s3cg.1593101616496.class delete mode 100644 dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$BsIjxbKl.1593101616524.class delete mode 100644 dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$DQzSzmpl.1593101616443.class delete mode 100644 dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$DgFwG7Hk.1593101616444.class delete mode 100644 dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$F1y9MwK0.1593101616469.class delete mode 100644 dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$FWInoz7F.1593101616518.class delete mode 100644 dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$G6i7FVij.1593101616522.class delete mode 100644 dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$G9Soc3Un.1593101616509.class delete mode 100644 dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$GNAHCepU.1593101616479.class delete mode 100644 dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$Gr2vHXQC.1593101616481.class delete mode 100644 dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$Is0bPu72.1593101616531.class delete mode 100644 dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$MDfVBZ3E.1593101616527.class delete mode 100644 dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$MXBuDuLH.1593101616511.class delete mode 100644 dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$Mr3Ng1rI.1593101616441.class delete mode 100644 dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$NcHVc13I.1593101616484.class delete mode 100644 dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$OKGB6GNl.1593101616448.class delete mode 100644 dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$PcPJF5oi.1593101616494.class delete mode 100644 dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$PzweZZ4J.1593101616473.class delete mode 100644 dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$SDJCxIlV.1593101616492.class delete mode 100644 dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$T91ZgJXd.1593101616497.class delete mode 100644 dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$UeMGnoGm.1593101616501.class delete mode 100644 dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$XOlg3lAd.1593101616521.class delete mode 100644 dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$XoYwdXNW.1593101616475.class delete mode 100644 dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$YNfVa5qr.1593101616504.class delete mode 100644 dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$ZGNUVBit.1593101616463.class delete mode 100644 dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$bOUDRcqT.1593101616503.class delete mode 100644 dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$bTp1obb7.1593101616529.class delete mode 100644 dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$coLFJ9Zn.1593101616457.class delete mode 100644 dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$dfA2UpPS.1593101616516.class delete mode 100644 dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$eMARe4Lh.1593101616476.class delete mode 100644 dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$eNVZsKqr.1593101616490.class delete mode 100644 dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$hiCh2uJq.1593101616488.class delete mode 100644 dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$inZx0SYn.1593101616467.class delete mode 100644 dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$l6vxjq2r.1593101616519.class delete mode 100644 dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$lFVZWOU4.1593101616486.class delete mode 100644 dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$nYaBoRKW.1593101616483.class delete mode 100644 dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$pAZ1ZMCC.1593101616513.class delete mode 100644 dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$qL94bV7z.1593101616439.class delete mode 100644 dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$qbo7evRl.1593101616507.class delete mode 100644 dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$tlsZFOzv.1593101616506.class delete mode 100644 dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$tnnsEeCo.1593101616514.class delete mode 100644 dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$u8urv8mc.1593101616528.class delete mode 100644 dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$wQexNdSn.1593101616465.class delete mode 100644 dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$yDUH5VxD.1593101616471.class delete mode 100644 dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970.1593101616438.class delete mode 100644 dump/co.elastic.apm.agent.impl.TracerConfiguration$MockitoMock$1166487442$auxiliary$GwsRwzTe.1593101616010.class delete mode 100644 dump/co.elastic.apm.agent.impl.TracerConfiguration$MockitoMock$1166487442$auxiliary$OscL2qTK.1593101616019.class delete mode 100644 dump/co.elastic.apm.agent.impl.TracerConfiguration$MockitoMock$1166487442$auxiliary$X025iH8m.1593101616023.class delete mode 100644 dump/co.elastic.apm.agent.impl.TracerConfiguration$MockitoMock$1166487442$auxiliary$ZWSBqb7R.1593101616026.class delete mode 100644 dump/co.elastic.apm.agent.impl.TracerConfiguration$MockitoMock$1166487442.1593101615981.class delete mode 100644 dump/co.elastic.apm.agent.impl.circuitbreaker.CircuitBreakerConfiguration$MockitoMock$1307674726$auxiliary$8Mb9HwGZ.1593101616379.class delete mode 100644 dump/co.elastic.apm.agent.impl.circuitbreaker.CircuitBreakerConfiguration$MockitoMock$1307674726$auxiliary$APBxdT8h.1593101616385.class delete mode 100644 dump/co.elastic.apm.agent.impl.circuitbreaker.CircuitBreakerConfiguration$MockitoMock$1307674726$auxiliary$BAX1I2Nz.1593101616382.class delete mode 100644 dump/co.elastic.apm.agent.impl.circuitbreaker.CircuitBreakerConfiguration$MockitoMock$1307674726$auxiliary$E9ZzXDgv.1593101616373.class delete mode 100644 dump/co.elastic.apm.agent.impl.circuitbreaker.CircuitBreakerConfiguration$MockitoMock$1307674726$auxiliary$RNW7KT5w.1593101616381.class delete mode 100644 dump/co.elastic.apm.agent.impl.circuitbreaker.CircuitBreakerConfiguration$MockitoMock$1307674726$auxiliary$RwZzUwxo.1593101616383.class delete mode 100644 dump/co.elastic.apm.agent.impl.circuitbreaker.CircuitBreakerConfiguration$MockitoMock$1307674726$auxiliary$W0PgUqhS.1593101616374.class delete mode 100644 dump/co.elastic.apm.agent.impl.circuitbreaker.CircuitBreakerConfiguration$MockitoMock$1307674726$auxiliary$hn0y2lsw.1593101616377.class delete mode 100644 dump/co.elastic.apm.agent.impl.circuitbreaker.CircuitBreakerConfiguration$MockitoMock$1307674726$auxiliary$oRvI9CJq.1593101616376.class delete mode 100644 dump/co.elastic.apm.agent.impl.circuitbreaker.CircuitBreakerConfiguration$MockitoMock$1307674726$auxiliary$onUvPA51.1593101616386.class delete mode 100644 dump/co.elastic.apm.agent.impl.circuitbreaker.CircuitBreakerConfiguration$MockitoMock$1307674726.1593101616372.class delete mode 100644 dump/co.elastic.apm.agent.impl.context.web.WebConfiguration$MockitoMock$880733899$auxiliary$3w0OHfSP.1593101616332.class delete mode 100644 dump/co.elastic.apm.agent.impl.context.web.WebConfiguration$MockitoMock$880733899$auxiliary$8UVTuVgW.1593101616331.class delete mode 100644 dump/co.elastic.apm.agent.impl.context.web.WebConfiguration$MockitoMock$880733899$auxiliary$J5m1M3Gj.1593101616336.class delete mode 100644 dump/co.elastic.apm.agent.impl.context.web.WebConfiguration$MockitoMock$880733899$auxiliary$SusFhZBs.1593101616338.class delete mode 100644 dump/co.elastic.apm.agent.impl.context.web.WebConfiguration$MockitoMock$880733899$auxiliary$X21vquhk.1593101616329.class delete mode 100644 dump/co.elastic.apm.agent.impl.context.web.WebConfiguration$MockitoMock$880733899$auxiliary$vAfycOnq.1593101616334.class delete mode 100644 dump/co.elastic.apm.agent.impl.context.web.WebConfiguration$MockitoMock$880733899$auxiliary$wMh2jraR.1593101616340.class delete mode 100644 dump/co.elastic.apm.agent.impl.context.web.WebConfiguration$MockitoMock$880733899$auxiliary$xryI3jE3.1593101616327.class delete mode 100644 dump/co.elastic.apm.agent.impl.context.web.WebConfiguration$MockitoMock$880733899.1593101616325.class delete mode 100644 dump/co.elastic.apm.agent.impl.stacktrace.StacktraceConfiguration$MockitoMock$1707322855$auxiliary$2KyRX7o0.1593101616278.class delete mode 100644 dump/co.elastic.apm.agent.impl.stacktrace.StacktraceConfiguration$MockitoMock$1707322855$auxiliary$5swnRP6k.1593101616280.class delete mode 100644 dump/co.elastic.apm.agent.impl.stacktrace.StacktraceConfiguration$MockitoMock$1707322855$auxiliary$BmSrhKdb.1593101616284.class delete mode 100644 dump/co.elastic.apm.agent.impl.stacktrace.StacktraceConfiguration$MockitoMock$1707322855$auxiliary$Tx0jQIZ4.1593101616275.class delete mode 100644 dump/co.elastic.apm.agent.impl.stacktrace.StacktraceConfiguration$MockitoMock$1707322855$auxiliary$X6N173wU.1593101616282.class delete mode 100644 dump/co.elastic.apm.agent.impl.stacktrace.StacktraceConfiguration$MockitoMock$1707322855$auxiliary$jIw4bUWQ.1593101616286.class delete mode 100644 dump/co.elastic.apm.agent.impl.stacktrace.StacktraceConfiguration$MockitoMock$1707322855.1593101616274.class delete mode 100644 dump/co.elastic.apm.agent.logging.LoggingConfiguration$MockitoMock$2051601129$auxiliary$0zVk9feM.1593101616311.class delete mode 100644 dump/co.elastic.apm.agent.logging.LoggingConfiguration$MockitoMock$2051601129$auxiliary$QWqZNH2R.1593101616308.class delete mode 100644 dump/co.elastic.apm.agent.logging.LoggingConfiguration$MockitoMock$2051601129$auxiliary$g4c944Dn.1593101616315.class delete mode 100644 dump/co.elastic.apm.agent.logging.LoggingConfiguration$MockitoMock$2051601129$auxiliary$jgjMka3t.1593101616313.class delete mode 100644 dump/co.elastic.apm.agent.logging.LoggingConfiguration$MockitoMock$2051601129$auxiliary$pTrzOMjn.1593101616310.class delete mode 100644 dump/co.elastic.apm.agent.logging.LoggingConfiguration$MockitoMock$2051601129$auxiliary$tiKQ5Tkj.1593101616306.class delete mode 100644 dump/co.elastic.apm.agent.logging.LoggingConfiguration$MockitoMock$2051601129$auxiliary$wG2reRR1.1593101616303.class delete mode 100644 dump/co.elastic.apm.agent.logging.LoggingConfiguration$MockitoMock$2051601129.1593101616302.class delete mode 100644 dump/co.elastic.apm.agent.report.ReporterConfiguration$MockitoMock$261936840$auxiliary$Cc88yyoF.1593101616231.class delete mode 100644 dump/co.elastic.apm.agent.report.ReporterConfiguration$MockitoMock$261936840$auxiliary$Ciailu7N.1593101616247.class delete mode 100644 dump/co.elastic.apm.agent.report.ReporterConfiguration$MockitoMock$261936840$auxiliary$FQIF7r35.1593101616244.class delete mode 100644 dump/co.elastic.apm.agent.report.ReporterConfiguration$MockitoMock$261936840$auxiliary$WscYjDAZ.1593101616241.class delete mode 100644 dump/co.elastic.apm.agent.report.ReporterConfiguration$MockitoMock$261936840$auxiliary$aPkuzhc7.1593101616239.class delete mode 100644 dump/co.elastic.apm.agent.report.ReporterConfiguration$MockitoMock$261936840$auxiliary$e5JFRF6z.1593101616261.class delete mode 100644 dump/co.elastic.apm.agent.report.ReporterConfiguration$MockitoMock$261936840$auxiliary$f9wdS49a.1593101616257.class delete mode 100644 dump/co.elastic.apm.agent.report.ReporterConfiguration$MockitoMock$261936840$auxiliary$fml0gMeJ.1593101616227.class delete mode 100644 dump/co.elastic.apm.agent.report.ReporterConfiguration$MockitoMock$261936840$auxiliary$oRNIUTuB.1593101616255.class delete mode 100644 dump/co.elastic.apm.agent.report.ReporterConfiguration$MockitoMock$261936840$auxiliary$qvojxnDz.1593101616249.class delete mode 100644 dump/co.elastic.apm.agent.report.ReporterConfiguration$MockitoMock$261936840$auxiliary$rjkBB8hS.1593101616263.class delete mode 100644 dump/co.elastic.apm.agent.report.ReporterConfiguration$MockitoMock$261936840$auxiliary$t827eD2N.1593101616229.class delete mode 100644 dump/co.elastic.apm.agent.report.ReporterConfiguration$MockitoMock$261936840$auxiliary$tC0jrLOr.1593101616259.class delete mode 100644 dump/co.elastic.apm.agent.report.ReporterConfiguration$MockitoMock$261936840$auxiliary$wXFwQ9X3.1593101616234.class delete mode 100644 dump/co.elastic.apm.agent.report.ReporterConfiguration$MockitoMock$261936840$auxiliary$yAFPWn8m.1593101616236.class delete mode 100644 dump/co.elastic.apm.agent.report.ReporterConfiguration$MockitoMock$261936840$auxiliary$zpZctcuy.1593101616252.class delete mode 100644 dump/co.elastic.apm.agent.report.ReporterConfiguration$MockitoMock$261936840.1593101616225.class delete mode 100644 dump/java.lang.ClassLoader$ByteBuddyAccessor$fvlmDHgd.1593101615727.class delete mode 100644 dump/net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer$ByteBuddy$ModuleSupport.1593101617400.class diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/ElasticApmAgent.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/ElasticApmAgent.java index b68a53596b..485befa2b6 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/ElasticApmAgent.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/ElasticApmAgent.java @@ -677,4 +677,16 @@ private static ElasticApmInstrumentation tryInstantiate(Class *
  • * Don't use {@link ThreadLocal}s as it can lead to class loader leaks. - * Use {@link co.elastic.apm.agent.threadlocal.RemoveOnGetThreadLocal} instead. + * Use {@link GlobalThreadLocal} instead. *
  • *
  • * Due to the automatic plugin classloader creation that is based on package scanning, diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/HelperClassManager.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/HelperClassManager.java index 1b9a50b592..c6ef38138f 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/HelperClassManager.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/HelperClassManager.java @@ -327,10 +327,7 @@ private static Map, WeakReference> getOrCreateIn } private static ClassLoader getPluginClassLoaderParent(@Nullable ClassLoader targetClassLoader) { - ClassLoader agentClassLoader = HelperClassManager.class.getClassLoader(); - if (agentClassLoader == null) { - agentClassLoader = ClassLoader.getSystemClassLoader(); - } + ClassLoader agentClassLoader = ElasticApmAgent.getAgentClassLoader(); // the plugin class loader has both, the agent class loader and the target class loader as the parent // this is important so that the plugin class loader has direct access to the agent class loader // otherwise, filtering class loaders (like OSGi) have a chance to interfere @@ -414,7 +411,7 @@ private static Map getTypeDefinitions(List helperClassNa } private static byte[] getAgentClassBytes(String className) throws IOException { - final ClassFileLocator locator = ClassFileLocator.ForClassLoader.of(ClassLoader.getSystemClassLoader()); + final ClassFileLocator locator = ClassFileLocator.ForClassLoader.of(ElasticApmAgent.getAgentClassLoader()); return locator.locate(className).resolve(); } } diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/IndyBootstrap.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/IndyBootstrap.java index 3fec20d115..689b55c20e 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/IndyBootstrap.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/IndyBootstrap.java @@ -259,8 +259,7 @@ public static ConstantCallSite bootstrap(MethodHandles.Lookup lookup, Class instrumentedType = (Class) args[2]; String instrumentedMethodName = (String) args[3]; MethodHandle instrumentedMethod = args.length >= 5 ? (MethodHandle) args[4] : null; - Class adviceClass = Class.forName(adviceClassName); - String packageName = adviceClass.getPackage().getName(); + String packageName = adviceClassName.substring(0, adviceClassName.lastIndexOf('.')); List pluginClasses = classesByPackage.get(packageName); if (pluginClasses == null) { classesByPackage.putIfAbsent(packageName, PackageScanner.getClassNames(packageName)); diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/threadlocal/RemoveOnGetThreadLocal.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/threadlocal/GlobalThreadLocal.java similarity index 65% rename from apm-agent-core/src/main/java/co/elastic/apm/agent/threadlocal/RemoveOnGetThreadLocal.java rename to apm-agent-core/src/main/java/co/elastic/apm/agent/threadlocal/GlobalThreadLocal.java index b4fc8af159..7770368ed6 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/threadlocal/RemoveOnGetThreadLocal.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/threadlocal/GlobalThreadLocal.java @@ -31,37 +31,38 @@ import java.util.concurrent.ConcurrentMap; /** - * Allows registering a globally shared instance of a {@link DetachedThreadLocal} that allows for removal on get. + * Allows registering a globally shared instance of a {@link DetachedThreadLocal} that optionally allows for removal on get. + * Similar to {@link co.elastic.apm.agent.util.GlobalVariables} and {@link co.elastic.apm.agent.bci.GlobalState}, + * this allows to get thread locals whose state is shared across plugin class loaders. * * @param - * @see co.elastic.apm.agent.util.GlobalVariables */ -public class RemoveOnGetThreadLocal extends DetachedThreadLocal { +public class GlobalThreadLocal extends DetachedThreadLocal { - private static final ConcurrentMap> registry = new ConcurrentHashMap<>(); + private static final ConcurrentMap> registry = new ConcurrentHashMap<>(); @Nullable private final T defaultValue; - private RemoveOnGetThreadLocal(@Nullable T defaultValue) { + private GlobalThreadLocal(@Nullable T defaultValue) { super(Cleaner.INLINE); this.defaultValue = defaultValue; } - public static RemoveOnGetThreadLocal get(Class adviceClass, String key) { + public static GlobalThreadLocal get(Class adviceClass, String key) { return get(adviceClass.getName() + "." + key, null); } - public static RemoveOnGetThreadLocal get(Class adviceClass, String key, @Nullable T defaultValue) { + public static GlobalThreadLocal get(Class adviceClass, String key, @Nullable T defaultValue) { return get(adviceClass.getName() + "." + key, defaultValue); } - private static RemoveOnGetThreadLocal get(String key, @Nullable T defaultValue) { - RemoveOnGetThreadLocal threadLocal = registry.get(key); + private static GlobalThreadLocal get(String key, @Nullable T defaultValue) { + GlobalThreadLocal threadLocal = registry.get(key); if (threadLocal == null) { - registry.putIfAbsent(key, new RemoveOnGetThreadLocal(defaultValue)); + registry.putIfAbsent(key, new GlobalThreadLocal(defaultValue)); threadLocal = registry.get(key); } - return (RemoveOnGetThreadLocal) threadLocal; + return (GlobalThreadLocal) threadLocal; } @Nullable diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/util/PackageScanner.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/util/PackageScanner.java index 6e2807e535..384cd6e77b 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/util/PackageScanner.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/util/PackageScanner.java @@ -24,6 +24,8 @@ */ package co.elastic.apm.agent.util; +import co.elastic.apm.agent.bci.ElasticApmAgent; + import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; @@ -54,7 +56,7 @@ public class PackageScanner { public static List getClassNames(final String basePackage) throws IOException, URISyntaxException { String baseFolderResource = basePackage.replace('.', '/'); final List classNames = new ArrayList<>(); - Enumeration resources = getResourcesFromAgentClassLoader(baseFolderResource); + Enumeration resources = ElasticApmAgent.getAgentClassLoader().getResources(baseFolderResource); while (resources.hasMoreElements()) { URL resource = resources.nextElement(); URI uri = resource.toURI(); @@ -87,11 +89,4 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) { return classNames; } - private static Enumeration getResourcesFromAgentClassLoader(String baseFolderResource) throws IOException { - ClassLoader agentCL = PackageScanner.class.getClassLoader(); - if (agentCL == null) { - agentCL = ClassLoader.getSystemClassLoader(); - } - return agentCL.getResources(baseFolderResource); - } } diff --git a/apm-agent-core/src/test/java/co/elastic/apm/agent/bci/InstrumentationTest.java b/apm-agent-core/src/test/java/co/elastic/apm/agent/bci/InstrumentationTest.java index ddb9d2773e..ccedd5ab75 100644 --- a/apm-agent-core/src/test/java/co/elastic/apm/agent/bci/InstrumentationTest.java +++ b/apm-agent-core/src/test/java/co/elastic/apm/agent/bci/InstrumentationTest.java @@ -38,6 +38,7 @@ import net.bytebuddy.asm.Advice; import net.bytebuddy.description.method.MethodDescription; import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.dynamic.ClassFileLocator; import net.bytebuddy.dynamic.loading.ByteArrayClassLoader; import net.bytebuddy.matcher.ElementMatcher; import net.bytebuddy.matcher.ElementMatchers; @@ -56,6 +57,7 @@ import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import static net.bytebuddy.matcher.ElementMatchers.any; @@ -368,6 +370,40 @@ void testPluginClassLoaderGCdAfterUndoingInstrumentation() { await().untilAsserted(() -> assertThat(pluginClassLoader.get()).isNull()); } + @Test + void testNoClassLoaderLeakWhenInstrumentedApplicationIsUndeployed() throws Exception { + ElasticApmAgent.initInstrumentation(tracer, + ByteBuddyAgent.install(), + Collections.singletonList(new GetClassLoaderInstrumentation())); + + Map typeDefinitions = Map.of( + InstrumentedInIsolatedClassLoader.class.getName(), + ClassFileLocator.ForClassLoader.of(ClassLoader.getSystemClassLoader()).locate(InstrumentedInIsolatedClassLoader.class.getName()).resolve() + ); + ClassLoader applicationCL = new ByteArrayClassLoader.ChildFirst(null, true, typeDefinitions, ByteArrayClassLoader.PersistenceHandler.MANIFEST); + Class instrumentedClass = applicationCL.loadClass(InstrumentedInIsolatedClassLoader.class.getName()); + assertThat(instrumentedClass.getMethod("getClassLoader").invoke(null)).isSameAs(applicationCL); + + WeakReference applicationCLRef = new WeakReference<>(applicationCL); + // after clearing these references, the application class loader is expected to be eligible for GC + // the agent must not hold strong references the instrumented class or it's class loader + applicationCL = null; + instrumentedClass = null; + + System.gc(); + System.gc(); + await().untilAsserted(() -> assertThat(applicationCLRef.get()).isNull()); + } + + public static class InstrumentedInIsolatedClassLoader { + + @Nullable + public static ClassLoader getClassLoader() { + return null; + } + + } + @Test void testInlinedIndyAdvice() { assertThatThrownBy(() -> ElasticApmAgent.initInstrumentation(tracer, @@ -894,4 +930,33 @@ public boolean indyPlugin() { return true; } } + + public static class GetClassLoaderInstrumentation extends ElasticApmInstrumentation { + + @AssignTo.Return + @Advice.OnMethodExit(inline = false) + public static ClassLoader onExit(@Advice.Origin Class clazz) { + return clazz.getClassLoader(); + } + + @Override + public ElementMatcher getTypeMatcher() { + return named(InstrumentedInIsolatedClassLoader.class.getName()); + } + + @Override + public ElementMatcher getMethodMatcher() { + return named("getClassLoader"); + } + + @Override + public Collection getInstrumentationGroupNames() { + return Collections.singletonList("test"); + } + + @Override + public boolean indyPlugin() { + return true; + } + } } diff --git a/apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-5_6/src/main/java/co/elastic/apm/agent/es/restclient/v5_6/ElasticsearchClientAsyncInstrumentation.java b/apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-5_6/src/main/java/co/elastic/apm/agent/es/restclient/v5_6/ElasticsearchClientAsyncInstrumentation.java index 44c9cdb1be..cf0d380555 100644 --- a/apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-5_6/src/main/java/co/elastic/apm/agent/es/restclient/v5_6/ElasticsearchClientAsyncInstrumentation.java +++ b/apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-5_6/src/main/java/co/elastic/apm/agent/es/restclient/v5_6/ElasticsearchClientAsyncInstrumentation.java @@ -30,7 +30,7 @@ import co.elastic.apm.agent.es.restclient.ElasticsearchRestClientInstrumentationHelper; import co.elastic.apm.agent.impl.ElasticApmTracer; import co.elastic.apm.agent.impl.transaction.Span; -import co.elastic.apm.agent.threadlocal.RemoveOnGetThreadLocal; +import co.elastic.apm.agent.threadlocal.GlobalThreadLocal; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.method.MethodDescription; import net.bytebuddy.description.type.TypeDescription; @@ -78,7 +78,7 @@ public ElementMatcher getMethodMatcher() { public static class ElasticsearchRestClientAsyncAdvice { @VisibleForAdvice - public static final RemoveOnGetThreadLocal spanTls = RemoveOnGetThreadLocal.get(ElasticsearchRestClientAsyncAdvice.class, "spanTls"); + public static final GlobalThreadLocal spanTls = GlobalThreadLocal.get(ElasticsearchRestClientAsyncAdvice.class, "spanTls"); @AssignTo.Argument(5) @Advice.OnMethodEnter(suppress = Throwable.class) public static ResponseListener onBeforeExecute(@Advice.Argument(0) String method, diff --git a/apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-6_4/src/main/java/co/elastic/apm/agent/es/restclient/v6_4/ElasticsearchClientAsyncInstrumentation.java b/apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-6_4/src/main/java/co/elastic/apm/agent/es/restclient/v6_4/ElasticsearchClientAsyncInstrumentation.java index eae0251fec..6627006865 100644 --- a/apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-6_4/src/main/java/co/elastic/apm/agent/es/restclient/v6_4/ElasticsearchClientAsyncInstrumentation.java +++ b/apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-6_4/src/main/java/co/elastic/apm/agent/es/restclient/v6_4/ElasticsearchClientAsyncInstrumentation.java @@ -30,7 +30,7 @@ import co.elastic.apm.agent.es.restclient.ElasticsearchRestClientInstrumentationHelper; import co.elastic.apm.agent.impl.ElasticApmTracer; import co.elastic.apm.agent.impl.transaction.Span; -import co.elastic.apm.agent.threadlocal.RemoveOnGetThreadLocal; +import co.elastic.apm.agent.threadlocal.GlobalThreadLocal; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.method.MethodDescription; import net.bytebuddy.description.type.TypeDescription; @@ -72,7 +72,7 @@ public ElementMatcher getMethodMatcher() { public static class ElasticsearchRestClientAsyncAdvice { @VisibleForAdvice - public static final RemoveOnGetThreadLocal spanTls = RemoveOnGetThreadLocal.get(ElasticsearchRestClientAsyncAdvice.class, "spanTls"); + public static final GlobalThreadLocal spanTls = GlobalThreadLocal.get(ElasticsearchRestClientAsyncAdvice.class, "spanTls"); @AssignTo.Argument(1) @Advice.OnMethodEnter(suppress = Throwable.class) diff --git a/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttp3ClientInstrumentation.java b/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttp3ClientInstrumentation.java index 05dba2f337..314b0c08ef 100644 --- a/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttp3ClientInstrumentation.java +++ b/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttp3ClientInstrumentation.java @@ -31,7 +31,7 @@ import co.elastic.apm.agent.impl.transaction.AbstractSpan; import co.elastic.apm.agent.impl.transaction.Span; import co.elastic.apm.agent.impl.transaction.TextHeaderSetter; -import co.elastic.apm.agent.threadlocal.RemoveOnGetThreadLocal; +import co.elastic.apm.agent.threadlocal.GlobalThreadLocal; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.method.MethodDescription; import net.bytebuddy.description.type.TypeDescription; @@ -60,7 +60,7 @@ public Class getAdviceClass() { public static class OkHttpClient3ExecuteAdvice { @VisibleForAdvice - public final static RemoveOnGetThreadLocal spanTls = RemoveOnGetThreadLocal.get(OkHttpClient3ExecuteAdvice.class, "spanTls"); + public final static GlobalThreadLocal spanTls = GlobalThreadLocal.get(OkHttpClient3ExecuteAdvice.class, "spanTls"); @Nullable @AssignTo.Field(value = "originalRequest", typing = Assigner.Typing.DYNAMIC) diff --git a/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttpClientInstrumentation.java b/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttpClientInstrumentation.java index b931e5576a..9b665eea24 100644 --- a/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttpClientInstrumentation.java +++ b/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttpClientInstrumentation.java @@ -31,7 +31,7 @@ import co.elastic.apm.agent.impl.transaction.AbstractSpan; import co.elastic.apm.agent.impl.transaction.Span; import co.elastic.apm.agent.impl.transaction.TextHeaderSetter; -import co.elastic.apm.agent.threadlocal.RemoveOnGetThreadLocal; +import co.elastic.apm.agent.threadlocal.GlobalThreadLocal; import com.squareup.okhttp.HttpUrl; import com.squareup.okhttp.Request; import net.bytebuddy.asm.Advice; @@ -59,7 +59,7 @@ public Class getAdviceClass() { @VisibleForAdvice public static class OkHttpClientExecuteAdvice { @VisibleForAdvice - public final static RemoveOnGetThreadLocal spanTls = RemoveOnGetThreadLocal.get(OkHttpClientExecuteAdvice.class, "spanTls"); + public final static GlobalThreadLocal spanTls = GlobalThreadLocal.get(OkHttpClientExecuteAdvice.class, "spanTls"); @Nullable @AssignTo.Field(value = "originalRequest", typing = Assigner.Typing.DYNAMIC) diff --git a/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/ServletApiAdvice.java b/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/ServletApiAdvice.java index 5aebc41fdf..1ddd6b51e0 100644 --- a/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/ServletApiAdvice.java +++ b/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/ServletApiAdvice.java @@ -30,7 +30,7 @@ import co.elastic.apm.agent.impl.context.Response; import co.elastic.apm.agent.impl.transaction.Transaction; import co.elastic.apm.agent.servlet.helper.ServletTransactionCreationHelper; -import co.elastic.apm.agent.threadlocal.RemoveOnGetThreadLocal; +import co.elastic.apm.agent.threadlocal.GlobalThreadLocal; import net.bytebuddy.asm.Advice; import javax.annotation.Nullable; @@ -64,7 +64,7 @@ public class ServletApiAdvice { servletTransactionCreationHelper = new ServletTransactionCreationHelper(tracer); } - private static final RemoveOnGetThreadLocal excluded = RemoveOnGetThreadLocal.get(ServletApiAdvice.class, "excluded", false); + private static final GlobalThreadLocal excluded = GlobalThreadLocal.get(ServletApiAdvice.class, "excluded", false); private static final List requestExceptionAttributes = Arrays.asList("javax.servlet.error.exception", "exception", "org.springframework.web.servlet.DispatcherServlet.EXCEPTION", "co.elastic.apm.exception"); @Nullable diff --git a/dump/co.elastic.apm.agent.bci.InstrumentationTest-original.1593101617596.class b/dump/co.elastic.apm.agent.bci.InstrumentationTest-original.1593101617596.class deleted file mode 100644 index 41658ffb5ee97a896dfe2c849e16bffae7b747d6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16761 zcmcIr349dw^?z>y%w`xMEH@$nj2gnR%Be!pB!G>E5Fro|Q75}YGGVhD_W;sXZM|#N z(nIUns;%~<2dyE3_2^w|50BdWzVG|62mOEFTsylvn-JRn=R@{4Gr!;Oz3=Z`zxR7H zJag>G6GXI1IaQ-K(J_tw!&FTNm^D_oO0LGJJXYgztjbM922`u8X{6Dy@jRY&4UZ?t z&qR$UaZQ9LYaHb%Do@oYO2;BRO**H`>=`Q0)My4BQ+bv~v(QPOmd@D`K1t;{8qJkQ zpOHr=M|f_Oh!?1Qibi#U>;TtA_|yoWrt#@qukk`|7|)A%u}0%~iSS%1cw$%wpP})Y zyi9(UM|eeqS8BY9SIfY&B7C;A=R|mo#%uW%aZ6zP_#t&f+#V!N%v-z+cmz3x2U{TqYLO*loW1Ld0UjKxHG~RM|g+Em+(#* ze5uB}_%f9**QkSzRZ|CFA&(7>yErbl3Ar*=-mTG2ky5utyJX-lzCwNyn zZm89`hb@(RHA+Z-Li!V&i0~eblia6rN~2!s>6M;dPNx>>m1uF);hzEPuFr9Gh0?b5zUMc`d1=l`<18sl-5IVy_i9 z%ZAk2)LPRWAbr#jNk+8Q$%qnyG#Z%|{V+ZI$gyxP(sWt?g^T4plQ z7>}FTY&e_Qfe~fX=WWgR_h&G7!~VG0FN!J~GP82mlubsir)WsJC)VOrc8 zPsdD{LC%WDjQ+luA+{5<`udZx4bJ1n{=N=-^ctpV@pNjp)t%4SX0*lZwqRESOsiVc zneJFNhuKhr1s%nTM#h@M=7FFVPiIW0Q>cW3DW&91a8ZEDg#L`R*T|V$a~aqkQ?#|$ z*lWa+u!flP41>n4wSa6LQ&s)K?M!2u(+QJlQY*~5E#KE=W;%?nB%VZD({UrY-N;yS z?>-!p>#^V`OT!U%#jRM0Svm@C7;S!edcy6Yk_KGDwTFz5kwc*!t2cg zU930XZ^2)o3M-!FPF23ma! zE~N_yXN3?hr@OHH5lm!vI*s2h11>Tn$Tko{m?nBmou^DE*|he@3}3ymwtO-vb_5mb zLTJ5}mD>c7^tYu` z-c&Q;4`Z5~O8a;1Nz=$m$T7X}?md}WA8LiC3gvbq7w@q}9DsWP!rQ8jv^1-kh99@m zA9u#E^t^HIRw}y*EZUypL|24q$s%M2b{Q|Xdz1NYE9GzgX6F3ngdL5xr4ng`;}VrX zCjF%|Amo4-X3X8OPSe=4#oTRX%v4-_XL@l^MsDK<=2gQ@TpQ>N09>;U6m4~vWi_r_ zdMVD$Cp%#X`Tq9q~A}#oFP-Y&0Kr=;%619KL`}tlx$i| zE%hh1!#V=UBi)L%Vy|8!;dz|1p&}St#c3g&pr&*J)5elVYb)=8g^Zy@>9)kHw_@`! zkX}Mq*ZgnRbAnhz3gfMmyFqROo`p%HuPb5LF=aR%)~!U&;(IjG{kI^(RGwR~kGd61 zwPgca5H&mtt6PpdJTRinHtUu#tqlw)v!vl2ukK9PXmCp`_cM{pDFF`QQLXL_$Y5(a zkL*H164T5QZFj&CmP!Vu(+B9F%5T;A4!%>TchSQtzfI@6_->sZp?5Q_IFW=;<$H9# zm+#Z*J@j6k@27)I=Yq;evFUke3TzYDroNSH#0a7;6R?lDJ-5|>K?+D;t;mfK7=iW z^)4;JAzD`X<2paVPwM;>Kgu+_;@k$EpOz7tm5S%PkT<5fRsMv`{iM#H;!msm8J$1N zpBt_Bgq$;V{yaaU@)vY|mcJ-o^CgwPEPnE((Her6MfowdL;^Z}U5u^39SMn?le`2><*)1f4gMz6hEZ9JPoMW`U-7T8dD#c||jR{jnjT7?8{7Oewj*W;V!UWFVsw z&~W7%m4Bo2Z~1ql6?*0B5Q=8M*ZB|pN2Uu#1#^Ug)ab&cpSVG2Av+9`>4II9?Y%nv zoPL4*lK-UhpZPDyPYZ*6!e&PF_*a$xrt{zVWtIQI^oo?3i**g;%&vSQF%U}(q>Mh) za*|FRF4lw|doKtOscY`Bl8KF0CJVi4g&Um)C1(9o_UwP@{BLQG>HHssCXG{AR}`g6 zRmSMbSY_NKRZ)@96fm|VlV-Oe)xDhQTRTQY@K=pLjp@u#)r6Lsn(_dM)zTx%cwN!? zPNufd*_C#9J3Ak~1bX3#5p*_*62BkLl(T+@7b`zjl+hX8wpo@VmeIr35>nr*AU4F+ zf<`xY^_p=Q$dtmPj-E_H%1RGr0?)4=wDa&gWX(8N*A1xYW*9@>sF>85O1UY#9c5&S? zYo_{8kCiA>C<)jh84S#jl4nCucn;Bk%?6;#33Y}DU8{1!@rQsh<2yd9`C#V?(Q}i$#A< zP1hE}2*W2AOaR9rR2N|@)9Wt@))L+4z@S@!GZhlg`h}OE)LfiA*yj#AWoVLQ!qu2+ ziMJ$iPN^?azC3nbDwe+d0J|wtw<=SRRX>`Yz%c>a(1zg^7*(WlJF_)VzUwg&;q2Fg zF{r2~W}81$-Lp;JLc+`Br3RY%mW3@$F*NqBQ8BW$;0S?tk$z}v7&aOejn1z z@OH37j6%)Her*f|I^nEjTq$e3*q4tJl8&@Yyn$ZmhX0a@WegT>7u3_`XNLUXd$a?} zET%mt_;zPB=R~(efJKh&l4^mf%tmERIayWa>PoFLPp5C7hOf+LS{Z6qC0k3`)~FNe zAUeHF|Iq30a`iX)`K$c=N!~cr6_PJ2wKu)Tbax0y7pDwYtEzGe?5H)J-XkA@+!fTS zJIN_uztTu0l4iEf8M!8~TevA_;7QR$DRG1h-x(P1yI>h$J67Ox6UcBuS!b*LLJaVX z-Ofi>IDg_>vclc7|NpBQcBw_3jsSy_DC)Mw6wHg=-hc!gS1COamsp9a)FGQMlU0}! zi)znBU|eO>JUW1{M2W_bBCp19rN~<`yTzL=yTwZ&v@!e*(LwZo5La6;gVD}wX!sb7 zJxbb+!!&+~^usiv;V?}cqDj%3L7Lq85sXpjL-?IYRrtLWUy$yiTDpurj62<-K_8(* zxceCX#^K3hXo-&aye@Tlo#yh2g4Yz6S9IziO#_7K!V%X&j`+M0bh^MXPBZaY<*8%_ zxSkfu^=L(|Gr)D`adWl6wHI9XfNOFDt}O4`ux^p-O8hDKE;4ng;oG7 zi$tN0svX4p1BewLvEn2CgpYW;i@453JQs*-eZBtNv`oHPm4ZrxvZ@G+D#x zVQaVvYq%LeUO(a*@CFck#Zb<}T4W6hm&*ON0Q#WZJ@kr;-gCFlg9_0z{z~?^+E^K_ zAEJdrBua@qMvD|^ecT`|?sT4_DHnSmGS_mxQ(lAJ|s0Qf{(HRxO&2$=z>37q3x`(FGy|C*0Xer$vfDj9X@T?Ev z-L4$W6OjWDhe)mvCx__F3c%-*3g8byZVy3j?}XeQ##iW%P$!~t0$&>x;-(q8fG_$B zxXoR_=86h)Vsvy_xSqq%7O8kP!ZqRZiYQptBFL`>xb*b-W?!N%{9Kbyf za4!JdivagkfO}~~aL@bTj@vcP`mbE$n{bV9!8N`E*Z3|Veh;qk{Q%5x*LcB)d8_Lh zZ6kPa)exOs7H-bzJWe0^F;M;lD1Sz)@B+A*enFe?0aC$%+d_ThMIYrAF3JViD1c*44Z;MCT6CdCDL)2C_jXbo>@Ge}~NQ_cWP)OH=932-ANFa1Kj82+H=d z&@PvAozJ-moSPla4N)XJ>t)m3P`v3nykExLe}MNt!TV+K{uegje+PJnB@qN8J3ih= zXrrXd=SN>TNG+x7W)xmoLE*28CgXZ4kD*g|?1;R-HVp3zuX%u=Kf#Iqr>lPSt`X%5#?Ev^^vd|JW_=q#?IjeHul z^XYU6*9V~Q4E39D`^$LHUB>34v=fLgEi7OcZZGo|aJh&25MAM2dNU4@^g7U`GYt9? z{7^{r$+UM0Y#JJg)Aj zno7o&A&Ot@DfeD3`O3qTXcz z(|F!blXxH?$(m3j_(>@3*9}AaD$sr{Xuobm+CL4YeeE!`ZvgF^K|6T<3&;JlP}HHwrpT>uxlkx?o;PDXJv%=`6@c^5*neZ*wsa(p|i@{O=c=j`j0m8Q9x|1&U1 zvFa~ifzoM37Po-K8^T%K02Vib#Z6#wLjZdi3zTRpvbYs2ZVP8|6rm1Ilp>`n#5{b>kNIL?F&EiboTSU@W#x8Q%=Zw)NH)`jLd>7!sKvAv)TDI!8o zLsVU$sR0(D;!s5w2WX8cp>-Hzs*pF!BL0mk520c)6@{z=^kyvj7PN0gdpp`Y(5BGd ziMAK*+t4P^-i3A-+Pl$qpuGoe9ol=*&P974+F5AtM>_-U+tEhRz5{I&+6T~Hfc8PO zYtcT07O#I$HL0e3yq{l1)%=1I%nlsVXbYk{)^&*$Mk{Le?;P{pmh34hL2 a2d<=NyLL&l{X8-E--1>jg5N##^ diff --git a/dump/co.elastic.apm.agent.bci.InstrumentationTest.1593101617596.class b/dump/co.elastic.apm.agent.bci.InstrumentationTest.1593101617596.class deleted file mode 100644 index 69dc0831bf2ed6f7a6355c0a49bde2c5e676d76b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16868 zcmcIs349dw^?z?dGMiz5u-u3QFk%vpRZbO(CIM_Tgb0Cfh&tIFl1({NC@~@BQBI&EV4~ zk3UL8Ym_A#y_!yH^dF{bI?SxG!WD8iLFGz~t5}sEApxjXS<^_PlM{I&>lz+UlFMX` zr*KV(r)nJLX(~_GC`>0qJVPdD%IaAv&(>%bom6>_MsqMppOMMAAwEOpc^b`^M^DJ3 zGebN-OvH;+K1-uI;p{Nih4}0cFV^@RuGhGM8z=G-UaCXZ5Oy*-83vs{32_8^6sZpQI^vO&g$5c*floXtUqRDGC8sxNGGAd_n&)Gm|ZWIJ3n_B(`UbX#b9EJdr7xpN+X}@YKAvR8NnY-fTp(sq|3E%DFA6 zfq_&q(`F=lN+uUIWis)eWM|5jttp*0N-->Oftu1imZD18t1Sj8p;Ws$ZYE+)(Wsfp z1e2NTgD6?PV0&(GFpagF4n@sDF;of2?DA97wiwyok_FQOEm4(C8&NaOw6ranikNVN zY&;q<1_vUB_)a7~FqnvJvL81M4s=@3>zHOlQ_21Do?O~;qitqS9DX&#w5BbU?ulfw zSPe78VWUU^WTYi%9S~~KRNAyB1xp|^#hBa$4i~^OX)ql>U}Vkh*))8QDcsg)955mY zctgZ~2B50-ad5VQsiMANC)0$MRLo?W(gwF~&kb~&=}x0NfhXa%RMbf9G}3YT?L3^2 z?TsT&mIrg#9gRndG%sog(aH?sX^?5owp=nBA24^uGjU*TN+wfwC<5ssOL=`ca2M&z z4aO0#FhxAlZ0?70HW|@gK?J`rvk2}oz)kzH%z>ShwW1FX#EqB`Jg`nF0f)ya0`Bum zrs+jbEMJD2ku9u1vVh?Y^`&|i)iQ*#P!tCYQ;mzl4Kt?2L0C!=Lx;juF-nKEsl|A~ z!ubJ+OYt_8K7xzvPo;3}HV`7S{A7J8glV!%)PBlzhK1`;#PG};Y0o7R;zux{E{Hb9 zGx9UWG*fKF|x#7R((+Hri{c*vIarNN;O8Q!=b&8Xa!62Y2qM z_*^(=E0x^@9&IhLvn$fHR1vZRJA_wSvx!_!Jn3!zX8Mwrn3auoBx5P0<06wlCxgW+ zpk!YZrp^74F4O4WX6`rBW-=AA|@P?3z|g=Havpr&{M)8?W_>&x$f`HG>)=$6K-w`21#P+me= z*Z*(iIZa+f3lrl>XM@}Vei{{5ntXqwm#fxZ^`>#cYDZe)FA9bskYD)lH zku_Wot6PaW+y_zOn{_Lg*82cTJZbd6t2+-q>faJe<4p8&mXAVkR;xP~I@q4dp}LTi z#5B9e+MNi5U0V|mG9O0KE7Y4 zchh@xels0mx;PL~nYye_7%bvh<+tekR(?RIU(v61`V)Oi-&Q~6jc!XhHo`G^D`KgNg7<;tzqib(48 zIDG<@9%>~|02h>L%ed52Ose?wrPHVA(@0&Y$gI+=ra6_$VmC4dx0u=9R4g;4ir*FD zckBEff!lgo#C^qGiKtIn2fhjkw2V=9m6{4qYR^9gkzga-n+br z53#b!AJ_R&eoW`b`C+EHW!E<9{0V{3;>l>P8+Bu{N99k-+E3~HY5t7LPw4zvesa9g z6L8Md`6+%{l1VE-@H z(2*&X{3V^g%wN^{Yy5R2R7pejK~O634iqGEPI3z{mA|3$H~Cvko5tla9)cq7rl|aF zm?TfsIre$*A^wiIC?u$8>ezxS%&qcwb^acIU-b3^m4B%7kNC&q%`fE}AUgkqe=4|t zrt;5q{ssS1=U>T_U-NIUd07Yd#Qs%%5%Yi?{jnjT8Ia9oO=-EnXEw=WR3PJ0&}i)% zm4B=A@A&uQReJgE5SnIx(D{%2C#Fls#pW0dsqv*tuW*CX0(KY_(|Nxr*?V>R1^p8H zCI4CHzwlpCpXPx*#AaIT_&1gRuJb?md6oak^rEDhjdTxX&F)+*HWY~sC5-{}auRkQ zF4Bw{YcKF2scY$tCt{o9=?v_u<$vfjEII36vS+`b^S@<0sq=pnno^~(t|&@{s!Y(8 zN~LOws;DSv@;tUC5@wGf-My^oc{`v&_^ZaB#&lkwX#!hK%{ed#Y3U(lqOR!t2B!AF z)#Y}0t2!UO1$x=(A+$G%qPQQ;l`~$27b!hfl;9cPwpmgmmf*wI642i(qc-H#yhXQm z_nA>R$h7>U&fawDpmZc)`arx}Qqg|9St!Shv&C~F5c0ONlEQzs`FE|O$f4upg>hC@}YfYrn7x$)uv6WNOPhK@GDde!IBmSfevUtMGrjl}QMidCi0k}7s z+Yxbdj7*`J^HPCnMOjAORhewAyBk>1lE#f?(RqHX#q&7)AM7>pvZkOz>ggaKmd+z= zFmOM&!B~_DAnI(l&0zUVURqd3g~kU4v*_f93xKSi0mKxymUl(agFA0E*KN0Est=4< z$ujwtfR&O#zzk@4Hu{xkBlY>Lk5xIL&Jv|dceF@E%=po?qR^9EW%WFQ)8K&g*3fP5 z!fq2a(lJ!F@*Da%U?g(pR>V`YAEoE{f#R0I2KAop>d%x{J61O~u)Dfc>}U6MEhUTr zJ~i(GI1Zt^2w$05zsKK8be@9;J?=YGq42D4*n?JcVew#{JFJqSS&9kAVrpEXC4qBF zeSz`ivHeo9_~i%4O_RP=iGkwv{6k0y00+U;nt%3GkuZaw2y&jCfM78h7ofj@RL}L52*eP@xaln;G z+VXDEahd4|h;%k1dr50gQeI(No&A;mWYN-Vq_>;bw5B-uk^88#cPZQMhgpnSO=+g1+$d zir`3^2fb&(McW?N)y75&z6)OPx`hXJ;0o+hX#5rI23`@e)*l4!qF`;=vz}=e2CCE# zWYvc4sjT#Jpw_ZhaMFi|c}3wqcPfWuD{yQ&zC+RWp-k2sKvmfj#S3YR--8A`BN5LI zMVh=@n9z_P_Lf-$W|iA0L1S2#N;;pktdop11FB8uuorFe>#(F(;AVS|oL#|N$r3pV zJtyn6F$`#@vm&@+(s;2iA15SiZJBrj-QcAEqJ<>{8g>fTGvzW%F8Chpuri0K|1{t3 zjOU!_vN^LRq9a9m*^_Uhy}gp0%jaz zqx9mE_a-6b>@ZbxS3xJn_5oF?N2Fq}i1u$W2CdiMw15udLsOy&q{!Pf+$r*6%^LA? z%o_0?31b9*BXk7wAHdxYUxV)5Ps=44*D=1#jlUzuL@5-hLPyFN9ziQ)?$ZN7__E2w8GPe zX$DxBDHL(8NXwqpmq%e6Gv*8N)PBRCQ*d zs%+zAz;u=tQqITlq5w6qyti{0-i1*N8(wl;Ifvjm6A111gQf6bWn4?g*V*CLztFN-V#JCepn$gYJXh-%rcw z%{~?)fh;`hvG6WO59aCU0UQTtE}uz9=)5wF&nFd(KM1|O4SIV!^!84C>i!UQA^KwY zq#>VK%|HP@=Lv9^Bf!?O2D7txctxWAkQ*%}wEzBecpgYLUyW4sf|sWNw(I&@nm#d)qSXJQrf$*+?gR%xwx}?h66T zt@+X`o&s}EgSltG+_PZrb7M01 zr2yv2Y3{#Z?n_|q%V6#+VD33E_f;_WwK19dvd7#hhsOE;RcL$*q48~m#&;1K-vf)^ zM`--O$7XP7e8pq)PA4?l$B5vH5xSrx-CWppiZSvNaQRbk`Ey!>x6Cc{OWK03obmzO z9vCCfd0bxYaJdK@1vpuEg4Sd6dy%EN4K-gIp^HZ7Vr7_`eD$K8I(~7y<=4sW&=`l=7Ev7XqK ztm-%pfW4{@r)~$)QjD_2EBu&T^OYk{_py_&V7~2%f5b8S-tg9N#|T|MLfeLEdza?~ zolwROTNy`b=LmIG4AZWzqqN(-EnH{A4AUM5MDFr+%yN_v#B-ssGjSrDPxH8zYH`1a z7t%6bMCWrIZRW+)!ROE(uJ^INH!yC#wK&SC#LUu ztiKP{mDq0ggh$lf71PPsHbT)|o&jRu-*oxk?=pRqdfeNBWk*RsY#{_9NQjGQ5;wzR zTj&hlNb_;OkT=s(zJ%6tD+IU<0<_UC-r^HtccA{i=LzBIKipe^ycN&uzYqNPyZk3y zrjOEqd+V+^N-}G+E|Wp5Uk?3uLjOCU|DDi(C-lDy`ri%x?}h&Nf&DAN{#8EKgZ2M? zkM-x^Iue9edMZzX<&@3x2^!pu3i+C2lx|xxOqs_iJ3_f(I?$E>6+Spjhq^{+NC2(b zmyFO$t^Zzzz`mC7t|AiAV~ho~0bAe?;XOYBc4-mVPkxL$_e5g12!7&8+YDL7+iDt* z0( zaVPKz2S2dUZ*WW(@n+L+QPSemNc;`OcpEJ?8!ZIEDU* z>qk8G>~I<%wT&aBJWLfBCOk}))&U@l_KS)p%)=DQ>)-CPbfCn#9M$YL{ImY;KGAC( z*4!X{7{nyE=dB@aM%ZbDs`EHCutn%NRM0LTt_elBjsd0u-5o-wVmcaFhv_vC_704% z!+1Bw*JDg#d;`WljBmsk!}unQ`!L>vu@mFH80#?JhjBi}`!UYJ_-2f=Funz27~@+p zHe-AMg+c4tY61pYTbdV46OVA_#iW27Ms;ZSUlts!sV0!_4D)etuZ$x`Y u;|2J?3M@q3P{D1y1%LKkC+=isr}O(pZ{}9-?l$l4cJJ;LxWhVS@&5tVFNO2~ diff --git a/dump/co.elastic.apm.agent.configuration.CoreConfiguration$MockitoMock$450365158$auxiliary$9KIyCM3k.1593101616146.class b/dump/co.elastic.apm.agent.configuration.CoreConfiguration$MockitoMock$450365158$auxiliary$9KIyCM3k.1593101616146.class deleted file mode 100644 index 0b27c67921632e9f967483f2a231a92105eec29b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 842 zcmb_a&rcIU7=0hDEM2SAQcyv(BztJ5Q2#1T z{E>L@5AcsNeoGtc!4qMVnQz{kH~YPLnP0zu`~>g>D}+O7LY0U?re!GlXQ7BynuXG& z?{(~~$h1kr$XFHq%LLm-p6blxCD>@Mu5Y&2+D`-FE_9-W9R|-{zZ^!}>!%)wP&pCj zB1}XYhdW0nN@gAkgzAKS>(W#lCCbAM!ty2WGM(fuq_Z|R6p17mNjyUBQld?`tE?7@ z{y5qx5lX_w?u-wt5_-MsyIO}(lx%@}H=4bP^U-~HcW%OMT}ai>bMr7;#!Zwx ztLwAlVUAFbRThbU=B(+Y2*SBz+@gqhxM#Ocd^##>$xc72E0{?c38Nc-%4_lAFqB_?60VR^=ZvX%Q diff --git a/dump/co.elastic.apm.agent.configuration.CoreConfiguration$MockitoMock$450365158$auxiliary$ALCg4um1.1593101616156.class b/dump/co.elastic.apm.agent.configuration.CoreConfiguration$MockitoMock$450365158$auxiliary$ALCg4um1.1593101616156.class deleted file mode 100644 index 07e452607de7b712c17fefd5a6697eb8010e2448..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 867 zcmb_aT~8B16g@*LYnN3^f#MfhaMiXxER7URFeb9m2eVKk2^bO|hGAF+r#obJm!Q8& z6TjkvKfoVlyxTUGAJ9!^@0@eb-Z}Tq{`vdsH-IO2$naj6Kx&?5N(B70AMjWvSs+Yu zq~hH2OqnDIjg{d=#@{mHgUU=1{I%}t*WA5d-kf+7m99;7-1FwQ9%rd;5 zoE-;q3>}q*ba829_@PSTEuM*Eif^oZrF15(&xMd_YOKF0-{r?U+v^OaW^3QW0_ra2 zvB)qxw!LYLmYhuAU~ta3&Sme2q2AoT*zZWR;o%l;ySRxZs%i6NP;^`E%&nIQ0hR+&;&T6fW)cOhS(Sja0Q`Q8@-#j}i+k)%$Qr-*UeKj60?eM4=e F`3J!@=X(GE diff --git a/dump/co.elastic.apm.agent.configuration.CoreConfiguration$MockitoMock$450365158$auxiliary$AOTobfsn.1593101616185.class b/dump/co.elastic.apm.agent.configuration.CoreConfiguration$MockitoMock$450365158$auxiliary$AOTobfsn.1593101616185.class deleted file mode 100644 index 1e9ca9a213c248e6e039813119ffebfd8d4abebf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 813 zcmb_a-EPw`7(HIgvW5bs>&Dm^Y*H@TPE&@U18E2e0vDh{r45bUaN&Ah=FPJw$3cUq z0*N1q3m$-nLVQUJT>9w}QP!(i>{@w2MC(cLx(C++%dwwD0d68RyYjs>ky@nZFXLvb1 zI|1eyHdE=JsjTSz@P)~|D;qCQEQ`1hGRv%sp2~y#c<=Q#!_r^pWwwdcnvQ~O?$oh> z#SksrV5qp<(A@6qGt53xM)}7KgZ4C|?CjUEg5?lZtWvCrJ(LXLztpnE8s$|*1+$n# z2u6rPr2AT-^%Yzte1}kr5TiQR{etEbjdck|AbnZfUYjZ&>pUJ%+L%3v_m~)tfpW_d~l&Q||etkQaGw0d7@s9n}ivu*mRo zc6I_RG3+MN9Z5GdgN%;Uejo=2){I`HnX^R(#pOj@2$^NZM*H%uA0EHjq3HkiP^8>b zncJ&k87mt$|G%W#Vfz?qZeZT)Rv)*H$Ll dH=haQ&l>JcBrEjUIby>0cieA3_=@^O^9Omr-C_U$ diff --git a/dump/co.elastic.apm.agent.configuration.CoreConfiguration$MockitoMock$450365158$auxiliary$D3AsD9bA.1593101616101.class b/dump/co.elastic.apm.agent.configuration.CoreConfiguration$MockitoMock$450365158$auxiliary$D3AsD9bA.1593101616101.class deleted file mode 100644 index 091900f284aa285d59e9fa4ecb40138c1df33e6c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 764 zcmb_a(M}UV6g`(#maYYADIf^S!#=e2VKGqDNKCN!VhSXZR9}3U4nr9nX2|R=0lrET zg~SIxz>hNArH%Ciy2;F)bLPzKxhM1I@2}qgUSf^#MY>oSk)&G2;`}TYePz>FI(w}9 znHQ;cHf}qw+W#}ro|9j7>hcn8w$?YcS}$6!B9UEcqlLeUb~kpC-PgUH07R&qiVG1N zVf*nx?^MY&K!H#lu^%&QMbD@J4+yLGe3ojHyO5dpxuLc&W*`X&wR?$n@uBis7=1n1 zDG^G-_p>uTuukZ7#@E^#LQ%2>{&&66895*H!@F}M?){}y=Xq`cX3KbpatRZ7MEEd1 zI|1ehi_$o&A|a(p66d3L>T`B-xW84#JZ3`Fus|sI%<^1sbP1E&+UoQz;h;XwWE$Nv zma!C~h!sw$?T!>7{1-w}S>yJLC}0v(2tkZE6MlXLUQc6&@l!@MM#Och`2*D(EPmrP v@Qg>1`3jH6SmHU?&U4MRrJ?rIcLw>lj3-0M0$)49OxV1|YV-L|REL^7s0+zy diff --git a/dump/co.elastic.apm.agent.configuration.CoreConfiguration$MockitoMock$450365158$auxiliary$DAdwndeX.1593101616178.class b/dump/co.elastic.apm.agent.configuration.CoreConfiguration$MockitoMock$450365158$auxiliary$DAdwndeX.1593101616178.class deleted file mode 100644 index ac1dbba272fa1a223f15b55179bfc7e0bab804aa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 861 zcmb_aT~8B16g@*LYnN3^f#Mfhbk(*zEJlka7!zB0F@+|QAR+N#m>ufCFhgc{0sU2) z_!S@g0sbiC-L|p(fNnB-=iGbt&bepy&);9a0X)MahWEloQu8ELBH|~<5${WrM#7o{ z)z2JHl{HbvI@!6H2{)}cQmM^D__Ve9WUckM^*rR+xzdWekKyZA@tKL`t_Q|YKIEr7 z(%kf;-u|H!sfPkXWt@GR8N>Ir^l*t`X^_uSrE?V`b1oOu;aU$V9>e^AqHMG+o#I-3 z8dgdSCGPs!F@3Pg(Ctocv~`A}APIDKz0n;jAI?M7xp8jeT*#B$HxJhW%pfRX3bPDv zCMUNMTSNiVxC|eR5IIgicY*d-aFy@_LW_hL#hK<;RKDZ- zPu%%T5O_$dNTo8ZyQtASr!P><>7^0<-4_D+vy6KqN|icKQO0lnK-gUQhRTTZ5A_=5 AnE(I) diff --git a/dump/co.elastic.apm.agent.configuration.CoreConfiguration$MockitoMock$450365158$auxiliary$DGg8cD2o.1593101616115.class b/dump/co.elastic.apm.agent.configuration.CoreConfiguration$MockitoMock$450365158$auxiliary$DGg8cD2o.1593101616115.class deleted file mode 100644 index 72c1fbffc0962023b0dd9c0e692c68baf405e985..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 841 zcmb_a%We}f6g{^Ion{CS(zHNJp-8hx(#4cVN~Hh`5`=_ODiKuJuyBnT+)nJs@l=Jc z0*QyzML&R#LfmNrVVOlO+1K~@`1<(RzkZ*b0(gi!gm==$DitHIWh{n+SR~5$SX%Qz zCz%soTN8JzQ=R{jXvfM!?QI^S2d&NfTdljT?MP(DI@Q8`in`B}ZP~qN0}!EdAdW5&NkMaDlLPhG$-L;7S=6Q7&N)R|zl9 zPfmbELX;@q9}2UhO?UEAk&sf2M%G17)!Xc2|K%3pF$y&S`XCUg6%Sv zupFX>YlMQ!43G839%25GHrhWS?AB+gKI0E+H}=X{#Y%`G*4XlneXj`NKSxKEb@sA| z0_L%R5X2c9%zwGS^GmqQ`EAZ>oDs)`<~LM-;QAMy1MA$2j90kdzzUB!cZp-pEls#@ czH*YcRot9VmbvU4BVqF=ZZ$W)qdMXI0YEC|IRF3v diff --git a/dump/co.elastic.apm.agent.configuration.CoreConfiguration$MockitoMock$450365158$auxiliary$DKEklDMw.1593101616191.class b/dump/co.elastic.apm.agent.configuration.CoreConfiguration$MockitoMock$450365158$auxiliary$DKEklDMw.1593101616191.class deleted file mode 100644 index 3d155b8e7bacbaa534aed985c080ccf2fef153ce..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 824 zcmb_aU2oGc6g^(cvW5bs>jrELhLnf4JY)zukcRd^IwYWD(k2aFc<@{=cyr^)v0?D5 zK;lEoTB?a%lMI&0WL7CmAH3G7b=9ewh)wXt;dvrp;c0pi4UYzT&s`c zlNv*f+tiQfgAInkV0NW#F;oRfptswd!BqLU9y(o|Vms$To)x|YxKhVO)N7c-RfgxY zlM`T(VLO$sFORv`E};hIGN+fdAIjlVYevtq+}YBKvWF2DLgu-#(G&UFzdd-lNgeoG zN@?~$%(F zH`d9zDk_-A0zxp#$WQupE406a%aq@ytVJ24xX}HA<~LmbM0;SJW|i;;%^O&uwcsvM iEV#7^_tj@gim{5D6Us7OJ4Z;^{f=ARyI;|qaQ*;vj^Hf- diff --git a/dump/co.elastic.apm.agent.configuration.CoreConfiguration$MockitoMock$450365158$auxiliary$DiTUnpJy.1593101616104.class b/dump/co.elastic.apm.agent.configuration.CoreConfiguration$MockitoMock$450365158$auxiliary$DiTUnpJy.1593101616104.class deleted file mode 100644 index 33e76692414ec07912ae665df7e4f533a1466e2f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 803 zcmb_a&2AGh7@V(3yUh|Hq-p67g(B@C35P9$6r`da2)$H+NR$>X99&}-m#rOny{P4> zK;lQ@zyt75h;NgWwD(xDKhJp99*_6W-(SB0Ji=YVTWMpJip*;ni@`}O5@mcWt$DAL z+zGF(iM!UR?nNegZskYqZBe3!oy`YZo%@}~k;qSVs)aj?p6OQyX0Ue_fC#l?@kzv~ zFiG4$I#$vLC=u!t_GNC2I7(H3S;G2|&%I6y7czIQFw_-kI+6s0#!#Ydysw-Vss21# zsSql{CHV<|uu16krgyb%LRqo}KD*iKO`MPJ!>bDuZueBGL6KX4>s4GqwSpPkAnZ-g zPJjhMD^dQ^tMrGbW|5FmWtnx+Q}rhQu>WFl|j+zEgzoe^yy-gHtM_ggMM31To_1_%AK-{3@<7 zzQd@&h`7$TzoGsE%U^g7Y;Z3#U*o=lRUQlNBG*D&8EapEWl*#=+!{-kc-t9f!uC(x LZr}Tk`dIT1Y{}R6 diff --git a/dump/co.elastic.apm.agent.configuration.CoreConfiguration$MockitoMock$450365158$auxiliary$HhndzgD5.1593101616166.class b/dump/co.elastic.apm.agent.configuration.CoreConfiguration$MockitoMock$450365158$auxiliary$HhndzgD5.1593101616166.class deleted file mode 100644 index 873e60a6689b49024a140598f596e895f2cb737a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 855 zcmb_a%We}f6g_T2rx^l-G%e6lDAFvFuyA>#AO%>EA|aGgsf`L77OrDvTqbtq@l>T> z1riU51s}jiA?`GRu*{;C?CW!Ue0_ZFU%!9+1n>xV7~V#^+wAg$tQGR~YJvG#yIQ9}I??bDLQ|`ryR7XYK0$i!$BB~Y4;VQ$c z^Rp9Rk>P%<{J;sRwv|upFx#3u*C25!OKdufgp?}Ftc#wjw|TPndXwQP*`L7&RsL8x zuN)ufk@}mtTg4KVLo{%Wq2zKyW3#o-F#kjw?VmC1G-t^^xSb3_}?hcAEVl;IGoe zulV2(@JAW%wvFWnbd%XT=iW1S&ON(-{{H$6-~}ENK1v%XBa&3hK=e)n(N$3zNE;pM zZWfDF+b9TatisC~Z_COPo!UHj&s)pSR$D8rm!8PZwb3HJ@HU@@^2BVdI3PmhSe%K# zh^QNM_K%fJ9TW)FQTAOHMPlD52UiFS{d|^cldF(f9Or^UVa$Ny5N7)nZG&wUYhm=K zL8U|}iMX4c@_}VSyFI?q)(Az(68LVv(H!epv*NT~UHm&Y8E*2=hpyDn~HfqTYT)bG4Qn0%!poxUb?{BfGz*ePQX zbr(fE;13Ym14VHE`}(MQOyVkB5F@sm|Lg+qui-l5hm7VK5yz?KS5&{_=1<)F x%n*3QtH`AauluO;JEzZa%;}{eefJB4{8_}(kW%B$6P$6IKj1Z=d_#4}`3I{g*JX-KAto3pTEC;19*hHgm#>(g{6TCF>2bGz;1P|Mr54PI(+m8d`PL)=|ehhXy-CnfYS00E^IT9x# z)FK&#-Tsk`GY`a}H_u=l+lzVm>%VD0Ihf8HFpj^Tn76~tA zXUD@5VMC>n7-r7O=TaODh@e z&(QXE=$$fo!iA7UVO{b{z7NO4cbos)9O<^}SioY87M2()E*Kg+-93ic7s@FAl3}Ma z&CI%cbu6PDqly*EFSCb|A^sar(O9Kms;FQVbBMtRQCjpdD>T1_+k_txY7t^o=X#fD ze#5=5GzV6xSBY;>zmGPJrFMa8sjZE*`xgYtwu}d3$s!#)LrmQJj)%R+S7?qke*k3| B(p>-m diff --git a/dump/co.elastic.apm.agent.configuration.CoreConfiguration$MockitoMock$450365158$auxiliary$NNSe8iYz.1593101616180.class b/dump/co.elastic.apm.agent.configuration.CoreConfiguration$MockitoMock$450365158$auxiliary$NNSe8iYz.1593101616180.class deleted file mode 100644 index 812681e7e120f1bfd072a641575d40e8e77e1801..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 869 zcmb_aT~8B16g`(#)-J1-0>v-1=&EgfSPT|PFh0;UF{ZYOHK-&$4AY?wE<0p)mmt4N z6Hw!WKfoVlyxTUGAJ9!^@1A?l-Z}To{Q3LqH-Kk&MEEF8ph6KRS_b0iFc1S3C4n^2 zz8<7jB-%tl(^%EKnDJj5`B^6>3;xr_+WJQ0Nn_I&>4^@tuwVR6r>8dchf@bcC?1Go z5riTd1l_#@B@+iZLTQqHlSYx)3zdUQgq2}FO>~&4kg2tqpr#1J5yc@a4k_9MJ!Q2B z_35ZmAQXfhq=)>&8ll~u-e_BdykrTyceU1@C?Czk)rCo}bt2VK=9`0S9_HW`FoSu* ztLe#cus~SXacgvNZ>``hMP}4XCHl&?hIaOSA*G6AWBqOQKK<0|Y!GtQ+OCHslwB-h znJ_;QzHLmX1k1ibaE?Wos_s6aT;07m@tCyb;TCSYxQP|+Y13%fd519jTt_;2LFiVe z>3?mjrELhLl5F4k?3nQ9EkEC!O3Jld5dyr|(hq3fIz=hxaULs5_g`ggt2ojITML%Yi}?$xP~NuHaB8)aNYxr7DWWOzM4 zJ06x9o=4K{b8$>3mJZCzB$0YFFnh+hk&I4G|lzKA;aP`rImZZ(5ufg*2ZBO>sa$q#0Ev&F(b*~|4lHh zY*Kbb6tIXT_+W%6P`bMXT3^F;!uJW)2r;Tl&F`q5jrF$4Jj9Gcafpd4QYrADv&^hN}IHw3obn41&`F89D6Ez zsv$9uxZnYJD8!ev&^fs-n~8=24D=eQ-03l zlF$0+bl)6L!~Y&5u(V;0D7=!F#9D+=cMg=7zdFO-GV|p;1VbiT9*cJXIH? zoeD#R+r$s(g$;&YZ+flmFq8#ZptGB;-o*K+AKIOtaIc0!p5?g(xKqVVR4bUlU52;Q zvlC#RVLg%Vjn14c8ZK_CU!0poTnL$E#zrsYq5rh^ev9J%=etO@r!u!w#R3*XG;oih zWWA=j+1h8AeXg`}FBtmGX-3%EuVNWXA<9^xIJ@SfWC;JKmDScLr!q>I#T-H~LKGhT zrzKk7!fnEfgc5Pj2xnz#niv`zUcq+q}a2e&FINJSioI0U5)BB*fTuvxZdTi1@(b`|=o zK%xb4;0N%d5aT2w{QNxUSu~DsyjJq4lBb~;1aGx|cwziv_&1bI2hC0;3d~x3#ZEecazOg`r@}W2p zekh{A@AeOsOf3`$l_~o+iz3kvm4!=$wGp4CI?P?j%$VFzON8NAViA@{5*_&=lSV7gsGFAzTQ@8CX zRe|DzxQ% znvEjv^8vKtPl{mwpKnsG@nnlAU>=uYgBbC+`DPdR`wFfye!ysz5pi5-d`IOR*MH*9 xSBAhtUPVrodEG^g-?@B+V=gaE>U9_WOEsXv& z?vw~65f8E>eqoc)>&7MZ&Y$ z+3~PMSl7u@o22n*-%v=YlElSfN4?43@4VO|6zYw=04u2aSjH-0aq6+-oKb=uULttM z!epv{K&aODPA@kRZ3no5t3EDcjc3N09ZlXP%s;~9C%|{V8IQFi4 zrwIQ4RFiUz^DLr(d7On0V#Fcy|6Jhbb2!iVCZknG#C4(h6_xL}_!H}&83MQX6uDLA pa}71V=kgVygE3|?ENkTTBH*b& z>W|bzAD|C~`fZXneSlc9KhJnhO)L?d!v!nU!h{XY}z8hNRm$xE=_T3Op@t+w6;BD>YG7WOXK-#lK^_d7Qph)}r@HzJHh z5`}vw7fL!01wwVe?q^9NPGaTZ8DXKzXHLht3z=D)8)}Oy1Og=zE=Lw@uiq4>P#NVHxw7^HIbChuAi!is1jtD6K4VIz<#PiZS>gMjRRc&H~S0 z;3ea?jB1RC>sa$Es^2mDz;j@cdy)AH_g9$XG1pFU&9$Yz_UMs8-sbVTFPY|JN0{-O NKk%lx{0-H<<_}{7)FuD` diff --git a/dump/co.elastic.apm.agent.configuration.CoreConfiguration$MockitoMock$450365158$auxiliary$RMMg2hhP.1593101616171.class b/dump/co.elastic.apm.agent.configuration.CoreConfiguration$MockitoMock$450365158$auxiliary$RMMg2hhP.1593101616171.class deleted file mode 100644 index 9bc73908bce98b87ced72bf37a987773cfd6a476..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 871 zcmb_a+iuf95Ixg`nz#niv`x7cQZSH&2bW6IE48DMcZIY#abAB zGOUycB@u_&0l%<9=yWDG+8UuKSpt8%-sp^#59i_P+&K5@SgON(Zw@Ybn1NTq6lMvp zCnv|j9HEsZ>P5=dC0axq>eITl=}To)I52JKmoKDLNn&GvUA@ctTN|r{LcOu$VF6Va z^H?Ozj-_)8qXY}TLU4|R$y9HTP_6Hroq0rB^KcE(0Q9M{Y*zXeNO1r zC+R?A+ru(yE{eF#ZnW(OMR5NwJ1N(A%|#S2jq`9pjCl9_n-}=`0xmMX!)TEaahz#> zMddp#|HO?`hQM7uMJ|>3+(eDWqo F{{TY4>01B* diff --git a/dump/co.elastic.apm.agent.configuration.CoreConfiguration$MockitoMock$450365158$auxiliary$Vd2B0qRj.1593101616123.class b/dump/co.elastic.apm.agent.configuration.CoreConfiguration$MockitoMock$450365158$auxiliary$Vd2B0qRj.1593101616123.class deleted file mode 100644 index 4f3c54dccad7e883c055b57f1a4ffba9caaa7009..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 836 zcmb_a!EVz)6r9(Dx-kJlnigm&6v?WRaM%<`Kq}(Uhzkmh)JlO1hwXN2HaOmPZC8b_ zqEZ2g10TRgA)b?nRN@*-yKmp@cs)Dg-+zAo0`MG<2p^OcI+96hl#nMQAwwOfLfQD( zgqf46v9ah{r@L2?;H_1kOltEIJZra}cG}zR7lF*qO=P6|91M=0?6yw#haQMf8Ok#$ zA{mFGe>l`C^-v&GXXt(w$MP`J9&jEu~e z$xew-k}k|f{6dS+>&@?KJA|TQ3HANxexCy&#>3$N}uGudAL=^b(Bk3z-_{t z`N{FHOxOx_$_AVnrT64$8c-mW(n(@nu&du^9}o6Agbqu#E)+@1;f`#~!MXnuzLIx; ztbV7A6|DNG;SQnTvY6+3V?bDZY50g&gnoUV4mSp6tYghb5gY7w*B)tt|If^%vdK0U zQNSXW;DfkhZ}~Sa@cIUBa{rLK8h6BXsd64vh#=0fWPhIV*dCAk>-UeJ0G{Fj;hnUxN=4?ijK$e$ED~jWEUh`x zN$!N#*2F#QRPSFV+P3mUds~#~Nw@QOv-_y~EE4&JPPK3!qS4{WoBhsy7Jvw~V{tCx zRG1{*Jvdg<2PhHhQ}%6cj5tVDfGdQxOFs8HEnLXlxx!FSr0G}^5E_>fZR3G*TBQ2p zc%?$92$$riyrDzr_hQ9}I-@~hmQ|{G;RA)tQ0d7=r71avnaFg(Q zc6I_R5;hX$Ie>F5)lj*NFN}ngD$A^kUaC?4ez3Fozu$p(+9X?5EMYlB1Gfkzmm40N zts!Clxi;FrAnZ108D48x#VS@pl(EJ^_Uxe|gnx6&YU`X(870hP0U?MH$HqUj#Pe&o z&iFo~1|#CS(Eft@H{AZjb6}l&nfV&`J6Ped&@OQ;w3Uf=?=ypW%PvdqWMF%!1G M<6isWSJWq(KSXKO{Qv*} diff --git a/dump/co.elastic.apm.agent.configuration.CoreConfiguration$MockitoMock$450365158$auxiliary$aHu8a2kE.1593101616134.class b/dump/co.elastic.apm.agent.configuration.CoreConfiguration$MockitoMock$450365158$auxiliary$aHu8a2kE.1593101616134.class deleted file mode 100644 index 7a3a0848563d951ed27f80488cc09fc8d6f0c9b8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 796 zcmb_aO>Yx16r9(DZnFd$(zHNJAxL{j!eQG&N~IzWM3E2*R3cK#r3crT#qHLv9J?y~ zDvRSFwiG7-g(;iaq;Q5#qlgjcQGfr;HNjv49xFh%@8oS>p9ITxWcb zQG*e2UFdv4{Tpt5;x*9ZQD(lz<2F`#F0{*B3vFehJ^0L^_||Y|B3a>U=a`8*-*LC| J;4A7A%^!9t)S>_Y diff --git a/dump/co.elastic.apm.agent.configuration.CoreConfiguration$MockitoMock$450365158$auxiliary$cUr8u3Cb.1593101616173.class b/dump/co.elastic.apm.agent.configuration.CoreConfiguration$MockitoMock$450365158$auxiliary$cUr8u3Cb.1593101616173.class deleted file mode 100644 index eb7117050914bccf4af016cb1c86a87e232a796e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 794 zcmb_a&2AGh7@V&O-DU}prfGqeLM!bd35P8zDV2&iutG>Er4ki|3kR<;i;KICylYf= zDvS2z&&geUP&7(BZ^YXSd7kMk*U0lrOn^y z%sWwPo5zWDD*2y@4y}BrOIwxb?qFwkZ*XU@9|?c1jTY`hB%ix|zni21h|oF}??r4x zp2bJ$sgh-Y8lgR7pZPo&sZjwg5H`oWFSV&$Nbg)_C=td?Bmto_mS`IvE2o9gA15mf zLPNOBpYetr!f-ghryUULk}dGv-QIBKeDWS%U72x@&ZQbvxdph=#6>h4Simyj$^7gD zSRrg@s(fs0D$G!%$`p~1QWb@D(L?ppzde4sM_B&rcAV#-7#%dRinS0OTqV?;&w1?j zP6&(lb*{??grn{}bL*Wnv4Qmvb!>8miG8gI;lGHA))t3TM-7WuLI`5SiSg&G@%$1l zGrq;B!-%*p^}nF~4c9*L9N6MsXTHV#I@Wotw5wbzZDXo^@tHx@HgIDqS>t0DmE80`d9}3jdhyVZp diff --git a/dump/co.elastic.apm.agent.configuration.CoreConfiguration$MockitoMock$450365158$auxiliary$eHiGqA6O.1593101616182.class b/dump/co.elastic.apm.agent.configuration.CoreConfiguration$MockitoMock$450365158$auxiliary$eHiGqA6O.1593101616182.class deleted file mode 100644 index 63aada4c07bfd34384a1ca16701c52fd1e6a18dc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 858 zcmb_a+iuf95Ixg`nz#niv`x7c(qg~~4=#cPq#`7U5CjDxk*e^*!*#Fpt zK;kAI_y9f%F-{`V4~V7p>^XBhduG;u{{H$6;2v%g-pVvou^3t{Lvb(&MWhlN$~4*2 zQD%hIX%e12Ctp3&Pd`3t^&AkPye|$# z7>gtdd%OEeS_cI}Wy-$Fl0@vr%E2kZ+KA7rj&m0>GbT6G7I8e5IE3YqM5ke287*Rc zJkFE|C1Ij$z#nW9y4~5ewnZpPw!mix_3qU9xF60gO}QsWQXS-Tb8ybX0=yFDut<15 zJ39`R2tlOmwh>Ydt+1-Ek5yL>wDpB#YM2`Tv3ire@4svj3PHW&VFgte%UC5WPTh7A zt0H9_Tp&1yBFl-Q)%|CTn$Y2}ng88X+XKP>HIA3kTP@i`%VTdA-o` zR3Pyqao_=XD8#o(AUr@U*`H@Tw#Os?`u*c4fCsoucq47BGLd^NV{v*Ci&PmOOKT2w zS~%ggHSy3oHT<86o?CgWy)8?0zu&tz=-=%>j6`v!GcDYCq|TGU)5G3}07PgUiT5JT zgh}I_gCiwBg!jdzvPBGVto zD>Xt*xU@Lo4Lw4V%fx^?#Y={r)6#duGDc6^%~}ImGEqK zb^RWo%^jimarV6g=>V0D-4hA z&OTxOkv7^tChW9l8D3|v5Kdh;HaMXwDwxLtLJ%X4jelr`=a+Dq z@hwIzM#OcY`vuK!xc-Ufzy|j!^9}ARSmm+QE^#fjwTbrCX9i_k!;Oh#nU9@gChUI4 L&F0GeA`)=v!5DWjY;5H9BIr}K zQd^`RcmN)%;#(3m73Wy8KhJnQtP$5UI79p(c;xuEb-gJV}%Z+tMl?t8d+v z0z-k@DDBXOWrkL3a8KK0$P2PScUNnzzVq&TXmz5`ZQlramF4E)Wf5a27BGZY44(&Q z$HN2#6-l>i5~q1b23!c4B*q3G*YG-r~*|CdlwTBL09$YBJd@WBXCVDvY0G#|$c!V82d zgc#M)`cIU9WBNPIfkoCi@Ba;ijEy3#;@PuZT;OZ HlzW;-<9pLU diff --git a/dump/co.elastic.apm.agent.configuration.CoreConfiguration$MockitoMock$450365158$auxiliary$k6gDMEVF.1593101616106.class b/dump/co.elastic.apm.agent.configuration.CoreConfiguration$MockitoMock$450365158$auxiliary$k6gDMEVF.1593101616106.class deleted file mode 100644 index e50b6be8070d64c0f26d6d5c6bfe0dbc7413a333..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 796 zcmb_aUr$pp96diax$HV+jsXIqBe@UTe7G3hG{Nw|#0N7Vamn<>hf?dcaMzNqm!O|Y z6aOSW_yPD(hTmn2Gd{0L`|CM9ZBI{n`RCVf08g<+cqeVFQZe#c#^P)ki$ob8OKU#p zBy+-RYvQhTs{1b!9awp)z0FJXq`m#P(|*)`7K!Xar&_pA(P<}naj-vl8Gs0t6LBu$ zRG1__IzCa-2PhD#Q}%Ucj5tnJfE$F(Yd-Tj&0WaMx!h1!r0G}^5Ng*FZR5UjTBQ2( zc%?)r372F;-mp#R^=9|99YRsE1-`q{>`k4I-@~g*Q*Qr4snU-ew>Bhn@e;_Px_4*(qZgD9FpmX&BW_Y2Q~I`<;;74BjrELhLnr8Tx4jpn>54)5lEoIBr1*F?80+R@Nn(PanQD> z8WKMe7d!wDh4_*d$^$f#{rMc9#OFKCuirm@0@%R=h9hBPsd?s=i23O_=BYG37S_C1 zY3{gJ*2IZ*GWnm0UR&`|d0Uj|ac^t8-+R>Cjd*^hwBqhO(tYiR+ZVe57(?TPf8w#` zCXM%xPo(exDh$mT`!+X*A8Q%l3d2Up=U(Z;g~**N3?*Fai6mfXl@ev+p>&FCburng zG1RzA^D(`!#V{DmueCjfsvrw=cDpl}IiK`HyUR1~`I(TXMQ#CZ)NvK{8WwPq;nn=? z1XyO+Nu^Kd<}#VlkvbpA(Fu>f3`yFYq(DMKA{#NMs=zC1!hAyHm+3eRhGEu=^eNx(~miIo13Dlq20} diff --git a/dump/co.elastic.apm.agent.configuration.CoreConfiguration$MockitoMock$450365158$auxiliary$nrbzqUgq.1593101616120.class b/dump/co.elastic.apm.agent.configuration.CoreConfiguration$MockitoMock$450365158$auxiliary$nrbzqUgq.1593101616120.class deleted file mode 100644 index c76e8716768ea65fda975851ebc167d60895c3a9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 844 zcmb`F+iuf95QhJ0LQULILVDs+#Hd-u>sd|NQ;+8^AL>B!DfHV8$@68P+TqdQhUoQJz}Iz6zYvm84FnSF^?s}>{w;jn#jWq zf_Ea~O!fA8hI;4XY&o$}#x30TaTCkDo6F2#&mF?_3(cp#B=qW&w7IcU#tLdainzy? zx9tZ-@c%oSRBG&H5d}=+GJFtcY%u@J1%AGQSZRoaJi;t0QEQktweD%^wJwkG`QgV*UZ(<=*}P diff --git a/dump/co.elastic.apm.agent.configuration.CoreConfiguration$MockitoMock$450365158$auxiliary$nwGKpHW3.1593101616197.class b/dump/co.elastic.apm.agent.configuration.CoreConfiguration$MockitoMock$450365158$auxiliary$nwGKpHW3.1593101616197.class deleted file mode 100644 index e76ce3edbde73240a764d7f627a4e835b520249c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 845 zcmb_aT~8B16g`(#)-J1-0!2l%=&EgfSPT|TFh0-_jaHjT0*1tgVHoP*Fw^V@qQ6QL zzv6>Gz#nD2+cuUT&`oCVo^#LMJ@;h){QdPCz(d?6yp=9eMkJ}0k?0>sqNi*cNoNmr zFN;O0osHTqR_(J)_|nPuI(2yoAGB82)?4>mk3x~1YNJK`F|;3^zUV*OUGqSM@{u?Z zkrB2Rb@z{yOg$6`l`;D|vsUaI<>4G*dBA6>Hn|I##c^(^EsPmTJi`1yqFuDDVl9mR zH0+cJB@y?sV}4W#es3#j^-$0A{NY;e;#qXgT#Lhw$6 z$yE1{P_6Hs-E1V<2(W}}KCWV!XU3TwOx__(Kh{>KPYB)mBy(=;1Xw}MM-jI;^R_!s z1pj}eNx8;h7E!=7&cg>W;)MBUF7WdOTx5Kk(IO+_I@A1$%6DA;iR+&k0(bZnxmD(K o12w+q@&&HByfl*UeqoSbE4Vq5RC)3gGk)_2!sfkisEjoK046Wq-v9sr diff --git a/dump/co.elastic.apm.agent.configuration.CoreConfiguration$MockitoMock$450365158$auxiliary$oQrfXJEV.1593101616127.class b/dump/co.elastic.apm.agent.configuration.CoreConfiguration$MockitoMock$450365158$auxiliary$oQrfXJEV.1593101616127.class deleted file mode 100644 index 9564ef95edd44ee50a6a84fceb9656f6ea70ece4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 800 zcmb_aUr$pp96diax$HV+jDZRwnB0eLK3oV6NiaUR@IvN@E*ZS=P-?jp?^@FJ67*AP z;-7>EKY$;~_`7Uz#^*I@e?6zC?deH>{l57L;02x%PNa=hDu!OmSX>TbktpM1Y0a5V zGAF#YCT?4&+W#`q8!JC(Z}Sp8Z*6Yxw6_FZO-I89Z6JA}1cKJz-wUC7M2+)!Jj=|~a~YPS+?zRmt+Ipuu152ruVcxLQ%2>zPsM&Oq`G2!>fxEZvR@U%RILL^JUycxr7-k5Duni zC%_^hN|fgSuC!EL`(Aa$Kt)1IH5^(Oy;jHBd9St`#wu1q6tTuhw%INr{2SD;vcVA*QNS$b5P}$SZu~_HJimwg zjMo{}7!lXG=2uj|nXvf- MPnyrZp*q(50T=exB>(^b diff --git a/dump/co.elastic.apm.agent.configuration.CoreConfiguration$MockitoMock$450365158$auxiliary$oqx9Tg8I.1593101616153.class b/dump/co.elastic.apm.agent.configuration.CoreConfiguration$MockitoMock$450365158$auxiliary$oqx9Tg8I.1593101616153.class deleted file mode 100644 index 3055992ba0f6bf832030aae6961c40b84ef107ed..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 867 zcmb_aT~8B16g@*LYnN3^f#MfZbk(*#EJTYKj1M%LXbLq#z>xSbjKg+tx>IL&N%2={ z;#Yj|2l%6mciYDD1G>rVopbNmJLjI+KYxGy2Jjg78QuvKNX_F!iGcUI0S{%A1j0ne zDoia;l!=0-v9ftS<8K@BK_w;&{-eg~!}Z2mW5efZUunhdnQuP!pX`MjFC8$3;t4Z6$(HAIY^umaK@P$D3tl;*DQl`#RlyU1n;MX5~ ILuthM2f?4`)c^nh diff --git a/dump/co.elastic.apm.agent.configuration.CoreConfiguration$MockitoMock$450365158$auxiliary$rvFiPylb.1593101616139.class b/dump/co.elastic.apm.agent.configuration.CoreConfiguration$MockitoMock$450365158$auxiliary$rvFiPylb.1593101616139.class deleted file mode 100644 index 6d12a7d951667513e1fbc6a81666c4682125bfbf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 801 zcmb_a&2AGh7@V&O-DU|iq-lYcLXlP?35PABDM&>ei0A=@77?lR!ohLP;^M3;ueWOZ zR7K)P;=lv&P>64nKzM*yvOmvwY>!9&_4~(908enA@Lt+j8IgG{V{tN!MWRw4OPhYs zN$!N#HjTU1sqX(w^xDc#+S{T;Tb<3v+nq<9r;*6dw9&$yNA7f2zd1L907R%Ai&GIB zktXr};8;l?phT!o*|&L`ih)r9t`OEoeD1X=T*%zH!cbQjGnNE|#z>-V+*eKuqc6rQ z6+%V0Bp>pIO+v3XyQl3C%91Vc-OW~S>U{hjUR|1UFVCbpDRK+2SjAOTE11I#!mHWY z39v+XkSO05srLFp?P_I?_QX(Sk&selnRU?%b(nwb?`?C`e?5;>M*;`#bKoAAUuBqWJ??1J?%t diff --git a/dump/co.elastic.apm.agent.configuration.CoreConfiguration$MockitoMock$450365158$auxiliary$vZ1jiPer.1593101616113.class b/dump/co.elastic.apm.agent.configuration.CoreConfiguration$MockitoMock$450365158$auxiliary$vZ1jiPer.1593101616113.class deleted file mode 100644 index 62827922838276e9f224c6e7251d9c2dcf00f4b4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 799 zcmb_a(M}UV6g`(#mabK6DWD(%$(pqFVIf*1!T4aqgQkj+1dRy~!#J)}ml-lUOOUU^ zL?Q9P5AdUmciT$+fKD=V=bSk^bLPzc{`2z}fG1cZ97r3hRAgSuSR5b4B2mW2(wa{? z$(``pnz(D7>i*9}udMu{y)8=gxU>Ffv$N6JibQ^@Q!U)r=;XsjU%yc<01;|^aU$YW zm?Yjk>?`R5lnC`P`!+X59HuJ3HNx_M&%I6y7czIQFw_-kI+O&2#z3NNyr-NNsXiO7 zR0tK}lKhB2SSR#)le^kBp)A<~pWSTr#?FWL;nmqOw{t4hagkeqTUA^~wSpptp5MSt z#w&~(jEL(@`#b6vxO2{PV3m8B`5O1TSmd$L&T%cYm67({HwHyp!o87Xp0}N1CTw5g Le*57M)JK}X)Hl{S diff --git a/dump/co.elastic.apm.agent.configuration.CoreConfiguration$MockitoMock$450365158$auxiliary$wFSmU0Ph.1593101616110.class b/dump/co.elastic.apm.agent.configuration.CoreConfiguration$MockitoMock$450365158$auxiliary$wFSmU0Ph.1593101616110.class deleted file mode 100644 index cf6117371d01caaaf3a3e2960aa17b6889cc1a0d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 829 zcmb_a&2AGh7#ugD+bjV>nigm&ZKXXV;ou-hKq}%u1P2rdYShAogX@~Di+3G)y-;{6 zkob`}@Blm%;@c!5mAGal`}2%vz2o`z_n)7?0Nlqe!y9S2G9vR@axqT0h*j#jwCP(N z=T3NSQ{J~u_0KcWb1UCzZ;KM$>vrz;x_7z{B9WhIqlNnzeRw)b4mvMS0x*W!sdz8A z5oyeaN2f~q040X{oZZjUR2&%<-~z+egwMS;g$tQGR~YIGW2TaTp)rwY%SXy-Vf3fz zPKBW&T%0HLLWf~6Slre27|N0?(6^hd!QA=uKD4_!=boHNH7;@saHWcis8+Cqs|?Q; zXD7fa!*;CvNF-yU_JvoGkWyutbIzJh`=rQ!jt93EYJasj(DRs>K*V=Q99!_lU zRk4Qk5Di>oD7iePx!F2oSbnJKh(`>=<|3nQ9agc4jSyvQQOtdNtQf+7DQ2}D%CC$P zma&2mj1YxN|8I%bmvEWzHlYR~Ms=nA74`49{+ZUm4vjMLH5xatL35#9qgrSyGwrJ{ b1d3-9H)oP{I(CVeu>AwK+PA->I(!SQBkT&JZ#-#>(h?#(!zz_bM|*@E^BVAFZ_?w%2`;pDC?`{pf$NFZxHF^*0WPP(2o> zBGe-3h28yQ8D|bkgxWa!I!_X@ucdWnIycU3oyGE`@Xf&$4>Ry8n8GaK z`Q+p{m?H!#jd)Tk^zd%B1|A;xB93L68tZS$-TY|#)f%A`H1|9#pzdNGi-g&++D&7$ z6s+|c!8sK=m)%1`J=i-x=7_Z6;RbHHxQ-?6Y4c>zd519lR3$2VM(75UG`+dwVHph< zW!&Klh|GZ`xc~i4s||Lxj1s1C5iW=k+s%J=iT9Uqnekmli;RflOzSIZ-*NRPZhdA5 v+~ZZ|QkB$9N=gw1{aa%v&w;p^$ZN&Kp{^H~` diff --git a/dump/co.elastic.apm.agent.configuration.CoreConfiguration$MockitoMock$450365158.1593101616095.class b/dump/co.elastic.apm.agent.configuration.CoreConfiguration$MockitoMock$450365158.1593101616095.class deleted file mode 100644 index b9b53fb0d31187a690006ae7169d180eee15bf7e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15607 zcmcIr3w#_^)ju~)JJTUR9~4T%Wf;$=mmiLV4<9el~+lk7)=!g-^G_5#`!MX!_ zLwSeew(~bAa40Gnce>&@GiGK|{L0{D$$Mo?%3wY$&|n@cWT0l8fsAX~2?mEPUy;{a z{Mu9o2SM3fD1jpw92L`JNh3a_TUn#L#n_rn4s3{&uZnrGv{B7qNgmoYhAmXPQrHPc zITDu8C`*y}Oxq}MG=p>ZXO+lTUAdm!Zd#`9PL}s%J*#F@tF6GXI6tdR+sv$C(6_wj zzxTGL6+>lkJe;7xaj=ZRc?UMfZKjvjGiYVE%^23RRt9Hg(Ai|@mawD(Co(vk?*&@8 z1KG6Uw(6FpM=gWFZL$S$K!lpY=k)BnCV!EOU$Pk!cd28?vaU<6BjDGRzr1`z8JrBu zH8=@YOj{UY@hPwp7id!VlC4f0)tj|^MHhpv15$`~QKvtm0+r|truI?rd&my%DuZgM z(Vz-y8O)AZj*XnAZyH}K2IW(Wyp=j`aA>a5j+-&!?b~VU&@(AeAGWFLQ;m*3R#gU# zV&9xztR2!;+w8S9Mw=B_t-%?zf{#cTnQ6YgkCR2ewjgj2e1>~WFN0oheZh~bxhM59 zcyOc~+;NUuaC+pOC1Ui|6>FN-;AnCeGa8M>?a|sYXc5+EEt)mV!OeEMds2aR4LZmg zhwzEaDXc&z{)QGBFx+u7W^B+?xCrE|b$cW^USCrN>jmqcVzC~LB-(o0hdLD4pg|w8 zKA0HlskCKm(lbaff|Eqs=Iu=i&PVrt0axd_hBhaQzG*L$JJyhOYty*Y2Zfn77t2f& z9RsP&RsBf?wrH@G%)|qXw`UUt@E~7^=Iu(@7zE|^GH#C+$xe`-Gb!QWE4P?dJf^$x zULo(rMkA3%JCVc}A z*6#Kp1wN+1<)p*>sa57@Yhjxvoiav7BY2p8LXiAqu}E%>k8V7x>MYD8Kdr%)MDidc z+39#0doO2wW7@2ZJGiGmE9iZ$SoF-v`i?eZOSJ-@*WenWN6|>TJ#M;=O{+G{fEhhn zS8d=C@DP+Iw12d=I<_NqN&{z)sUz-|rpkWv(_Y_@H~%j8EZ za@(8rR+Te?H18HP|EE|qjme(Ky5Xt`1@6`0zF-}<>gi0@HCmiF9zhgOy@g}Buol%m zj{9J6GHryqJTH=rjF`9?zAe=JPO;Q%T;Da>+FLuSz;`uxh}2wjU@9r_eaw_i&$m>k zp~tbz#RCouOy!eW-%xFiCvZ}KD75|GVrjd+r*EKjebiLoM;bgfV?(t#j%DbW^!|iF zSyTxArur%TK!eBOXH?qoXyW>@xs{4d?KV|5;$fP3#z>}-La8T(X8%_#&AMw^W206t zy4_!B@HA<5tYFGxcP!FuF*8YW0R1jH0}DgmFv^jJv3P74PupJ!%D*laWoK-A<6xqp zOM%~L@SIP%6$`swkE6$tazK5bl;g2Pea6P!@;gEK_r;>z+|!y^l}(|Q|DeH(MEOMY zU83^kq4Tn`aUEaEMl9m9RePOKaJS%X)I_@Uy= ziy1z9d7(t=3!*WzGR5o5JW<4%^2@0E&J*>qRc3V~&eY$8!hbK8!kg;1)|xw7#ufO7 z2CtLC$IVbU$o2-4uHhveD|8QMhZFIJD0-)V3C{m67U#~S9p8~?t5Xx{Q@_`fPTw==Vhj zYwDskRSHuyrV{;SSahWUR)j zDr1KUg%=e|;n-%kAzRxTRoD?4TTBWcJXJ$P-4wQjpL4Akq`0~zrfDDNTryi*Q|;E2 zv7-g~V~Rz7!d;(A_B3o(*l`*=p2#0*dM$Y4HX3&(Z1iQW8T0l*)=V@Q84Jml3$iB` zi)>qSe8P?!TNQS)#+DP=<4vz!TwP+mfmL>$E+9mVv=7p8qq=G&jigr!(x()Q^o9)s zMuWL+hr&+NSS6A6(<8jT#Ejh-z;vR=OqrpHoDr?J4Ki4jkR~Jk1 z=DtBEI_%jBTcfeHr1)V|J9tVjg*A%+g$Gr}{sbt=(Z+0*iz>AVmD-D?k`=Ks1GPIE z6t+%doutw!G?(IzasT;8QZdiA@DveO9IR+p9Bu2{N3FKr359LY7{B5uGrg%L7_`u-O^B#HWjv6V?$)$x#)`ap1h2XWn;q>IcyW;Fn=pfx!ayt zw?5surB-3*XzW}fx5&e=T6|cM_YDK3+z0p-#EKzN7?BO$j85Qy?rzbMU|pTA+gVJ5 zQidU=x;kd@TYVKSz@U4oe8bnY!jd=1qGe2ZtBWzS- zCbLwyLST0hLv&@PrB%sc@CbHsWnHnrOxyPr1&&r*YiehaP1R2ZPN184>K~-FL`3+jNys z%09S-JwfGzQuamRjb~oW$q?s=*L;~l^*+*g z8TuLVz8ml!3{`up34~-MHVY0X<~L&Gf+O*mMNFb6=?$+6);HsqJ~UwkwMQ9dr@-8b z1q+S|?u2E1H^L@jgUgdIi0O9=GQeem{w|ExaqxW~Ba$&#t04GI1}720scUtyWF!!{ zK=8!ck<$Bx_)eG-Hp59hp}>m2`#&;9JxgtAEP_V@!`YB_LK!_3l#km`IkV zPTc&5)j-VCh7&T6!I>F zWSy@SM*E}aoVV#@ox4S6ES@dHb>nyTuj^#}uMquT!F(YX14;U^twPPeGB{yoJzLTp z9ueJN!xB2}lD8VVN`_+HVlMy5-9L9*VI@%s8lD$KuuerzJHL`4>CNUd{M53vgy9AX z&7@_xW&ABArC2^dPly}Uzk*^=D!zu(@^5qI4wwmBr@Ir(*f(YIv(S_=WO^nR&zo~s z!5$ospJ0{(ehP`7-7NtHRA41@@T1rgppP^0OHY7#OX%XAKk=%So)n*Q8SjVr6Yu5t z6RWEHd8Gfu?-{s^pKtIdeyqZuc;m>QSd^qE)djeW*AZOCiVT-AiQuyTq=F!qF*)Mx zSbpYDyh7to{2Gk6m-=#}{~Yw6c!$ONVWpk-JIH^o^q*Kv;lE=($@}4D5SKBr=JJ#N z^J4#rce}hDlL@q0KictNI0P-IptJz(SAx$u6)X3^VK?Ew0Nc?y`Rym8wHf}lw4&k; zSbRGiHR~QY=59D{Tg7fD-vbr*!zs7=pi|*AA2c5!1e#s32UdwOO97o3l<;?!`(rNl z$7~W~oP2!TgAI2G6) zqgEeO;T!iD-?**-WeAkzpov|u<7R)(FeC|K`W}J1kZ|uSRP&w46wk^K@CO9=5&~aZ zs3y${#$X}fj|%YR1pYV&izQZ~A?Y~?{SZDGpt}yS)2GA$SJD8V$r}Jc7+8gXt`?wc z2=s;NAUt(KK-US-^#uCLbP%3vAv1EL0Nq5On>px~U2uD_^)T=avCA%j+e5f-a@@3~ zPHP7vvJmh+0(>8V@0Wuy&I(cUK>>bK5U!r%^7b^PJ0Zq7L*UjD?o7h5mVD!! zj{gw}6U`8ChX8jHxQoAZ<_e^UF$65mK1OFB6K7um81sP;bqB?phX}l-z)Tn*hv3c@ zxN`}2UIFeM?0g@W7nR%zi*FN$71LSNZ=VV;G?z5OEE=)o z)CHvrSz^{b%-qFNJK;Sf+Bxx4Bszh}&L%M(i11Mo$8zG0B;rx)5B2hbV-_+mCmuzu znVjg7n9Yev6327m`6N!{#Op}ho)cdqaWW9uNhIzFM0k|M3j&cHPU1TQ5qe2{S0FMj zzB>@%O=`tcU#P%3NPKS~!V4r`m=kv`WEU-D7w6T}nowVe{v4KZ?LRAN_>@eQj>%EQPS&G$+NzdvYxKssaJ4w}Cs zwc2+Bt34#8_Wc0;FuL|ay?+oi|1cL*<=gzxp!r9+10}!t$HBXQB7OHy1N6_N%%2F* zPfE@5RDk|PE+5N(_b++#RPu$Z?ineIUkBqnD`oLqz~Z;k=HCU)zn5Cz4?**bQXO6j z-u)+O^UFc=pQX)z37TJ(Qu|x*?!QZ${}D95E;Y*=!QlUrqTlq<=o%IjuA7n)A6+s_ LhAx@S(E$Geyel7D diff --git a/dump/co.elastic.apm.agent.configuration.MessagingConfiguration$MockitoMock$2120571386$auxiliary$7zXGjbP9.1593101616358.class b/dump/co.elastic.apm.agent.configuration.MessagingConfiguration$MockitoMock$2120571386$auxiliary$7zXGjbP9.1593101616358.class deleted file mode 100644 index 1a5a0db5d1fff713874dea5df3dd599e7ef2c783..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 892 zcmb_aT~8B16g@*LOV=v36jTs_WFOk{uz-@Uv7&cRf88SOdfM2DF zABhkC0DqM6F0IxFpOj5z?%XqHX3xE6zW@084L}QP3_{pgYMy!}Vt#TQ^F$gS3u}&4 zk~!{`HSw-YQ=TZ3bpC{+T`S%xZ}SjsZEkHmY;8Vxv>oy6Olig4dDOai^L#kyJr2Ma zDnowCW6ezxzZ?vu@Bs=8)d{+n8N&x!2Drwse2HgX>0FD*oXcf(xYi>|z)-uSC>!^s zQ(UWy(N2k>#9fjd(}4|!y`8Hru+2~uB!m>y8#@#IqpE0kZh}2K6Y?bQVSw2(uA^MS z6mBu}u3A)pIfm^-`jJ<%XSF87UO8@3E^-BZPj1TOJmNx-pVmchyWJQ4qk{oMmkRlJ zMZa!tmoblp5H;LpD7ehfTyN|%Og~XZ`KJu8>;LhazdyHjW50|gEQTmznVjj^L&*^S zZhTrB03(DN_Q`UY-NzCu}zGDdNx`4QF6xbvRYz$%R*;T0Npu}E{y kou`;{OJnZA2TJm93HQd71-f>Mkg)j$_nYgVP#trA0t93Fq5uE@ diff --git a/dump/co.elastic.apm.agent.configuration.MessagingConfiguration$MockitoMock$2120571386$auxiliary$NsWtl48K.1593101616353.class b/dump/co.elastic.apm.agent.configuration.MessagingConfiguration$MockitoMock$2120571386$auxiliary$NsWtl48K.1593101616353.class deleted file mode 100644 index e39fee36086821096c299c1e71df92b9d64421a1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 788 zcmb_a%T60H6g`)u3>i{LLx4h|Wig8+EQUvUNUgewV9^4tKusiej%!8+W21Q>%2$Cz zi^QTI(2uHmheU)Ah$Z{_93NZ9$Nu~8;SYccifB2SGd2%9FMtnnrp5t^|hT_26>yuo4#a*j~!vgZ{B0Ex%>;&zUDujlhWV- diff --git a/dump/co.elastic.apm.agent.configuration.MessagingConfiguration$MockitoMock$2120571386$auxiliary$SQ1Np2ea.1593101616362.class b/dump/co.elastic.apm.agent.configuration.MessagingConfiguration$MockitoMock$2120571386$auxiliary$SQ1Np2ea.1593101616362.class deleted file mode 100644 index d530585d4f43548ced57da9e60e91cb0938d9115..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 885 zcmb_a+iuf95Ix(3nz#niv`x7c($+wdKDY^jA{8M)L?2Kuv=Ob8x6Ni_wrstT?X3!5 z1rj&$zz6VAh;b5;en2d($LGxP=*-Oi`TOfPfGs>?5W)mf^CVRw;DfV(hcZe7VWPeY zZOl_;qF_%Z2@h2iwl35Do)I5ZYO>%r8_mrpTaCxhp8DKgD6M$>(Qh9%_6JSL9WaLC zDL?0d=1~|NbWf#79po5F6ZXhP5$|g0;0nXakhiJQnHOQ>IJ4E_T8|_S!{Sh)Owg9G z;#z$gWeN-h9*6dfe%NF<+MRZT+YEU@M#w?6wmb1Z>WZ=p6Ylkekb`W74sLjugIB-| z<{92j+mwR^hK=OCvAWkXTGM%Rvz>j%GJ*0e#fG14|j0a#cixm&Dci6%Et_|&sC(-7Yy&J z)89eu*uyF+F7jBXeY8wZGPwW8ofIoH=sa?m#Z|aqglPJ7>2vgb4c7@jAhb+~QJkxP zMd>?k{=~h{1c8Ti@{}slxsM9HGx-w5OkNnvPreYyo>i=kC1t8SLyTMh0l&WS4W+T> EA1?>)Z2$lO diff --git a/dump/co.elastic.apm.agent.configuration.MessagingConfiguration$MockitoMock$2120571386$auxiliary$ZBMbCmAS.1593101616360.class b/dump/co.elastic.apm.agent.configuration.MessagingConfiguration$MockitoMock$2120571386$auxiliary$ZBMbCmAS.1593101616360.class deleted file mode 100644 index ae5cd364d18790e2bd4098be7388cdb8cf1fe847..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 826 zcmb_aZBJ7%7(Hc^%Pv#q7@!~u$^Ed+50{r9CJ+-`B++b9S%iddT`f1oyO#8}1pHN+ z_>%a6AHa_?K6jhWAFw9v({p;-oSydf_pciOyI5xs!o)J=nNuR>7yXzgQoC3feWns` zxl=~R9hqf3Q95b;Pe&aiJ}GDN5^Zg6ZS3rBZa;q(aet*!#qD);`m)n&^$)rM7(?Zp zU-CHRI*H%*&ZTex3JldTd*rp|y;KIc!>~Hwy;EuKMR;p-TP>cZLrK6;8%UIiyV5G2 zs?Wof5<`jG#P?~#2E$Q%(ihxgC<-z{4(g5e*#GcWv^qcLUR?=!k!KiSu8difOPIoa zhIf-T6=0sBo=EpP(Z>S@Aa3H`X?{*FWrRc_O{hp%(G2XLmYBx4PMOK!nnT z_$vHRn7}{iUMOiD`NR%r6&X9HdvgV2#jejrxGQguG-U?4Vj}4gL2|#k12xZvRHA>nuYDuZwtzVgX~A zB77dTDF@SpTA=KIS9H+(cj5^t`3j@h`_gQFIy^t^a`6AW_cCtj#O@X`gIO09ydmV` z)No&|oe;)9YNPEQ;kY{bFl#48EMmb$9!nf-BRW$A_bIoew8Dwyk;6D9;DQ)&jQo0Y zJbr~q#_t$a7!lWr`aQ~znETFSV1-+r`4YEzEO4J`XSinC!a#d^#~}L_@pd4Y3nwv0acaNn=9OM{EK&<_oUz3oX4xXzFlB!m=HYujV}!>On`H_mOI3wfID(7_E4Gw=$S z!Ysq?q(wQHV_1!kY^r;krg!1kn~0m3i%de>>{_jTE(FfDLQ4!yVjpaT`n2GfK^%^8v&3OJ!8@ zis5Z_@@~`)JS?N)B9DjUOw0BpgL|>~xLBc;=aIuSuEGT)L_W~<&(ZfaTqpd9&>|s5 zai;zar60KY3-`Vd1Rm4LQ>jSjJ}UIi=nE7xdSOIA{7N8umhoUjDO2Yu%DDBP@as>% Iqcr0D2l*WGjQ{`u diff --git a/dump/co.elastic.apm.agent.configuration.MessagingConfiguration$MockitoMock$2120571386$auxiliary$zSH2u0qa.1593101616352.class b/dump/co.elastic.apm.agent.configuration.MessagingConfiguration$MockitoMock$2120571386$auxiliary$zSH2u0qa.1593101616352.class deleted file mode 100644 index 069fdc13b93249c43a1d38ee5b68127bcd1840c4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 814 zcmb_a%}yIJ7@Uu!EZGK{hCoaBt+a0(pITTz~h zN~M)J@Blni)o)4Epq^vN{ygKcH6Hu^&+Q$6ExaN~X~HNGeWzq7F3v*{N7{wb=u;J^ zRybvJxF7ZVB33$XKc<6yBfltTvJ$k`Tg{EF^*7s_fk-b^qJ;e(Ty;LQ(&ks;fe58D z@lAw@&~f;wcNR(KAx9{W*uzw7(Muu^PYBBoeCkw^d6B8LnXR@+l7Yk{R30SCgq_GL zk*KS|N`X)iHcroZLz8g0H|`7W5b~0Zu!CBCZ{&Y)D_)%)aqll>bdhD~;aL$+Q7m8r z&j|JFKX}rEc Hd8qjd{D<3F diff --git a/dump/co.elastic.apm.agent.configuration.MessagingConfiguration$MockitoMock$2120571386.1593101616351.class b/dump/co.elastic.apm.agent.configuration.MessagingConfiguration$MockitoMock$2120571386.1593101616351.class deleted file mode 100644 index f1419f36b794f2db43519bf8182c214207a381d0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4734 zcmcIn`Bzj`9RFN|-Z)K(lbWen)|4<-!=`{k8jC0jh@z-y=AHL0!^6jW@D>x>_kG{@ z?bm*2RZpEd{oEhb>3iROGYs&iQxcp5m%Dtv`}Y3)>zCgFwBs9w5#7lNi+jGQ=lINY zju(aP=XA%OFpGi9ebcdXL&EcT(X@-bmB*POM?Y`+j$AS=%`Htk+nZZEc4=S?o!3lN z!zzXr$1Ucj75SWL`@*%kmD63JycWG8r;mVMd$jYFvhqQh|G9@|H09T6NOBh?&&k4nPF1}+7%NP zsZB8>?L<^=!6r#%GYP)fHX3ea(62dEssvz$2NzAtW-CyTp%VuA;jPhL9a92MMBR@HD9!-g;~ zWDt)AGs5lVmcOcJJ#bxFQA+aVD?=?pq_7F7N**uU!Gg^{p zLMua^ZaFr|v@n4ptr#-%0pBYh4m;9UGmh&EW8X#TI;v6{+N&j4`*f$nXPZ*!R5`SJ zWwT}2JKhA_t}^XV z_BrmN))GU#E4wGZhp5FDo%03Vu+O)q(4%b9yRtU%JB}WkGy8`$>`S6w+T_O2sb#e_ z93V%Nom7()BaUUsbalDyaa}r?!kGJ_ILFXW_PO@TQ=(JVG)|y99qlbC3@XzMt*mL< zFP+>!RT$~ia5#w(X_}tpE2rTol{PZxK+$$w5gGMJAOb;A*Ao+9u{@_$`OKX#Ekoz7 zF{JkY5LcGpX6j88S7XX}Csx*Y<2^%#-s$d94X2VAm&Uurn{)!p=%x6f{uyfAZ5S?% zHet=9DIz{0+Wep}(Mp1!Rf0dXvVxBuX&#wM(ZuMe|Ss zCc~O}ANJD608P_Wu2zRtO^$I44^$2n(S%sp`$N=%-o9iId<2gsaS4wlus(rf37lcr zEw%b@-C@LaE|`XJH9Vcb-UN110Fs=2LbtdpD5RvkDLk*3y^z2`YGYxW&hYR(uXU3QiLm&>TspX z7p^oCge#3p;YxjwXsjbzH{eG4`z7ttM6ZU{>#{qpV%@j&ALxc09Nljx?u^bhXR|+H z;}6)f>Njlt6}O+tUP0z6vVY*7?;_B>xGw^&rB#CJvRBcjXx7TuB2Hh@aVDaB5Wd=RHH-Ea|!p{*vn}xS-GpJY2qZuqvder)$#}Nuo=a z@%WFh(p%F{ly_U`?#c4*B<-Fm@6M&4Nk3cO^%3qlwR`bL2i$6kL}y8bAZ_$MXe OpOyo#lKAIJ2mB4Fm|pS# diff --git a/dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$0B2hXgmq.1593101616460.class b/dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$0B2hXgmq.1593101616460.class deleted file mode 100644 index 9de8753aed85a8dd2944d15e50accabdd87ab690..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1226 zcmbtU-A)rh6#k|yRB+YGj|d7BUHb=QYlWbxF-k+=qJ%^UdOK_ z+Mcu3Ge2v$4itBVvUdabmDGVz_S$--vbO$cqipl&RC)z zCAlZx^-jhZ#<*%mEpjL`Y}PN@P&F3tK2=;Fl-z414yZ6Z9FkEh?uXpfGVq-PajJI& zZwR$5v=%BJ<^jV@EKiLlAJzkdB~;3#F;M@7VhnOt%H*KDty5RDO|CSiEnJ2*h6H9T zT)|bdnze8Z8MC@&;W{SFYM$ZgMF*C~B11mZT7E-gt18RnHA3)Z%}E~!V>O`Ba6EXNfr6@E))+LMjuOG>L&~z3PYlv?^~(H z>g`|PkeEZs6N~gm=Z6;Z$c-a~A~B~HGz7!=88O04mM%SoBt~%w29+>8p`Ss*u%xSr zBVA1lA)N%nB&O(mnRZ$7V5Fx?pK+69j^sAU!WY;d$quZL7|acld&tq-_$~GQrvKsh n;*;^CCyzoOo}+XlWSJ{{MX9vhpXTek-aD83^Vjzu0QRxVFp@D>Mx zobD&nV<)6?(P1p#X&>h%>h5(q-MvTq?MP%ZZM1OjqfWQ|=9QYg48RyFXX0FNBkY79 zjn9z>r-jiU<~JpV zl5mr3N)Or$&xV)%P>-P~DKTYiG>6N2=f9xa^(8l$NtJjV+WX9C(QBTRQO8z@BDSfW2l1(52>)~|t?W>riYQg3l1&P`LQL5DhI_3CUr=3Wegm>R&6xlI diff --git a/dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$2Jg1XhI6.1593101616525.class b/dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$2Jg1XhI6.1593101616525.class deleted file mode 100644 index 758f191baa7515b6949e655c4cf3e5a4e981bc83..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 823 zcmbtSZEF)j5Pl{XdnR6OY-?<-eX*X9L}Am=T0{}G6kkXwSX=zMU6;6Zb9e0BN%dDL zSOq`$1N>3qToX(Xzhz-|c4wZMedb|*{r>S2z&36Z4pc0(m1$;_kV#+2o{lnM`iT|0 zgYc82|Jq5VUAPyk_a=)A7jABKI-6Vfx7(r2PmMLweGEG5$Yx~Stym#X&Sq*>-L{N+iNGxrSUKO=23t}T(4pQ zO9ap55$}!W5n<|K(d7~0uraxY|9f^7D^-+nlLg#~-)chjuRdvQjrAzQ!xS!}%I8uc z7k|=HAr^ZNgc;QMev5C5?1}w!>nrBj>fdmKK63yF*~*-pWn0Dy??u7Fupk&K_)@@m ctzvD6T4I#Pk(t(ather*aqqils1JdE0DXJW0{{R3 diff --git a/dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$AAA0s3cg.1593101616496.class b/dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$AAA0s3cg.1593101616496.class deleted file mode 100644 index 772cfcba15f965203c55bfcd7208c926f7f368c5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 778 zcmbtST~8B16g`(#mabK6DX4rXCi~FV#Ay&hOpURUMjuK@EWzhtm}~~7yPMfv1N22sSp?*EcrbZgm2gpBQVTI}LihUMGE{LJvf!9m#J}SQ&+4 z_uxpY%tMJ#pR!-_D3S+Od$>nfz2)=FSfS!b<<1ptY)fm$5|7Zhm6%uzwKLM1v++)a zP?0XoM|_||_&B)hg}Q{Y;=)|9*&0my9p8d?=cio%MCl|mapd7q757oCUrF5FcE*QAo&!2WW3E?%j_xCz!anP{)lMLM|7O?1}fhUBL%Ojqf zt$o7mI}@4gJ)z(HN4o!e4Bgg#70X!iQN}9!xE&vAg8#>+w6?|ul~KYh=HP=Ev1|Mo zOT2!7hm2n^YA_;>bL|V%ukiFcuYollW#(%V!Z diff --git a/dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$BsIjxbKl.1593101616524.class b/dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$BsIjxbKl.1593101616524.class deleted file mode 100644 index 853c825dbde9dea3ebda53bd1472b8df02307101..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 890 zcmbtSZEF)j5PmindnR6O`eJR3eNj(HqOh^F6sd?d6ctkNd~ zJpFt)cxQ!F);~4!qfSib{QZNyz5Rp7hfQCkBOPjCKl#V;tNy6>I&{GpDt&PwcqpQP zcY1v#6BiD{$~5~njUv$tm5VD3TjPA1=#Wbj$<*46MoWZY4sjXQ#t?1znX+1h`g2|> zF_eT2(g8haG90((9q4GL;Uk9S_LOpq6p5)`lthde)#@|Fd`haWP9L0%q#7pLL@t&+ zT!U9a0jnMsaXq`;V0bn!T^BbQs)0&cCiKnhI-rB zfX4}~dtf43s?Bt#_o)>bU!HfHM4j;rZ>+h{=gJ;=*u0ZbLr~kZ=$xYq?!xAbK z-=k=QJR|QlzG01Q{X1@dp&0PVid0)6+rl=T8GdtuU!1|e|C-_H+rjPxRwbB2nWe@L Q+-*F#q}~rNu|5I*1yaE5lmGw# diff --git a/dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$DQzSzmpl.1593101616443.class b/dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$DQzSzmpl.1593101616443.class deleted file mode 100644 index 58003dee67291ce67cb1fc748d486b79efb244d3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1138 zcmbtT;cgN^5dOA6Pw-T%)LOL_^rS#BhuEeu7^7mNX=oaYslT_|3)y+* zCZdTyd;lNHIC~JeL?xPDGCQ-o^UcmTGxz<+*KYu}aF3xYeA{t(5K3Y5LEq**#|v%Q zAGr4R`1t9d|5|b3C~MajZ)NCfWo>LWn;V-Cw;C3YPNXZjI<=m^8g_?-G{6}0$NU|) zUGDYly~ATiga%R!3)9p6$n*H2>lnx|ti) zT{#@zWErwt^`brj8Vt`mXC>511ia5spAj(N#8wi+t~MN%gfm2Uz`gmXarCXBGI1}*34+3ZL9Mnv TqPOZJ+LcE1+Hz)u)d}V&0!21_ diff --git a/dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$DgFwG7Hk.1593101616444.class b/dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$DgFwG7Hk.1593101616444.class deleted file mode 100644 index 646caf72975cb9a760f7abcd214b216ab6e08af3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 910 zcmbtST~8B16g|_etSyUFKt4o1)YY`rWGjI*rpBlh;b9307<}<%7$(iYc4sp?tLU%N zL^Sb%AK;HN-ldJ*7l{d*+_`h_J#+WmGxO{B&vO86JYd)tI*<`htr7uG`T_6B*aoVf zM8W1D{W9slHC#yJ?`UzNtj?XkzOlBpzVWo(@_BZuBE`)o|8?)p$F27Ei3`S1JLVsF z5b?Md>>eIVVO=;3Q={x&7RUTBk}j?>EM4R?tD->YSY*cJG`c*B1`wBF<^rO0urH0` zk@`HSR2VAU^s+u3XfeDBuR2ht(C`VvTsWfKB}LLu^CM{pRTe~#c-0#P$Y55gPLGal zo(h>*rDGR2J&ePvpoD1;6S$G@ZZW*LDrpyU3=dPwjos7Q(uKt-7x_PZE`&@|ZT!&e zZg1_h5{9r|U|bxkafy0%)H5jb?|4|i?JDN6$l#bPrnTNUU>JL*Vr8E*ysux<_5U7O z=U;peJgi{3iZbrfg>?0iWT^htJFTsfzGXNV!*x_?U&`gDw_nP|C+~n^5;cnNQ#4PW zk@uS4Fhe%`9e2J^4ESVas!fqCVVTw(zc9p46!7o9=6L#6usVb-63n5@Wb+5^H6NW( L@5g7D9RmLV5-9VA diff --git a/dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$F1y9MwK0.1593101616469.class b/dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$F1y9MwK0.1593101616469.class deleted file mode 100644 index 782807843ae9c045658a331c36625ed517e8a0f0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1139 zcmb_b{c00I5dUowdnTTmw(+ZNw6>=vQMoje|M7)!}hZd5Y^p=jqDfM(gdfss+Z7Kj3e; z>v6y1?(7{%VJxH>X2#OzvG4OePg*#`P#nl(qdZpxzKFF>%4l%U8=6=Qg@K6*+_uz; zd+Oa#lV!+q-HE&8P-S@1JZ(euBLPj|uj*%GRLk1Z^4O6G3@^RYPvBzdl$S_H3Vb;bu*jc1-$;NqHNN!hbTtq&( zU1NB3T0kt!Gt5VZYts(7?{Fbx6b0I8>YeSaRyAaJU;1At{oQzZ!q7==bsI%o&tU;K z7}7fS=`59Z8Kxd6UzvvtA5N-tvW>4#SkON=;+S4{Z7gFchYW7fIy8cP$&mX!?I^!U zGtD54DV#-)cq$n_{mZFj^aDpu#i6}$% w1)`f+qBHR+j(n#7@Ok+u@u6=S&d79F6i}#q#qG-7KIORArzH3Lm>XID0;GIF0{{R3 diff --git a/dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$FWInoz7F.1593101616518.class b/dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$FWInoz7F.1593101616518.class deleted file mode 100644 index 523943fbd7f7f166a7cce2051084e48e2f1ddcf2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 973 zcmbtTU2hUW6g>mj6-1>iTCGL#;BxJ6WS(X>hpA%kg2;fWY{IOze*F) z#0P(XKgxI)h-`fEflcn*nS0NfIrrY|uirm@0%)MZ&=JOyJ|Bcic)TBYyeD<&si5zB z`;+jie(>IMA+398#77mH*tt8q_4>~4vqsJ3(Yf*!x1ZeBhqu~XG+GWAL*a}Mx#x4; z^Nvr?qzE0P8J1?zcahfo)Rzt}F_gyfC{(^Dj24l#v5Y47{RzZjD2^e@cpYgK_tnKD zlV!+q+lvAs)EHj3=PhV2ap4n&mG(?>lR7eJ8{U;RiKvUfHv+Ej#VPGl@o82km*>+p^m*U-K`0AkFN(m3RzP~5>eX~=>3RXkZaEnlISJ6}La~9@yepWWK`l4%T?hwJRKRZE2=`{fR+7)^T?xS>y6G-?|2HXE=_ao3#pP+TORpZfCU64%wdu6Y*rB- zmI!Nln46^Ieki0=!=ZIy$8}#E9<>req24$Nu!5?OWvmhwr?U2~HA=9k8wBrMm`ruw z5vui*|L2*I_5!TomXDiQ=bkQ$N1cxe^G|fF)2D=XeRfY9#{o7_^HIb-_V>WPRRsT^ zeZz8%jVq#nd0d7MV#IFpBQ5av6MTM-l}xR$m+w$>72YK+(rxG0iX#OvuWsf*j)&FrqCPvJ{= zuZh*f3m?FTGM;H0x)-J~5;r+>X3l)y`J1ype|`S}U>lnZZQ)tc;X!B%i~Bu`ccmLz zcF%XL-JyBM?;R;Fq%sdY@!AeOt<3sXtybT9xLq}QbY?p?SMSWdXFK~%>Ay&UF=S8q z8*Vw=?OKPOQz^m}5)89r>+{HUdB>3{OfxJF>``btmhfB=DW%hBa>p4$QVh8P#P+PV zR5o|)_rsesLz=5@)FVQb;c4rl1vO$0?=$3EJx&7in{?9q3yZW zPA8zF2-`k)<1QXB%*DbqsqsO}<0n!PDk-Q7RDP)(gA7|rw>QSw?Vbtg>o_Uo4P1ti z#uVlZT)|bnn>TO`S-rb$;5ugYZjs^1Mf;V)GQ+Ciei$ivo`cDSkU`)nv!xFA_YSH) z!-rC=f^jeZrFk(&@otqT_>xL_%G%XybC_(yzzS9~DBvzbLPajErSdVu%p=>i!^aFC zOA{Ra-T%>urTTwrJ~pt1Vg^Z+X;x)I-Lr{V5D=EFStdrKyrs<=_|}nWCu1#H0CDBJrrrxyp<6z{|Da7 m&zeWy8cHL0k-|-rWufv7mCAabVr}&4wArU<^T|GzMx5W1N>Zl) diff --git a/dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$Gr2vHXQC.1593101616481.class b/dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$Gr2vHXQC.1593101616481.class deleted file mode 100644 index e0b85a4f10d9aef0b8249f79bc40f071cfd54c1b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 802 zcmbtST~8B16g@*LOV=v36jTt!WFOl4FiitNgE4B;z(WZU2=Y3N<7RMnW;44>fM2DF zABhkC0DqM6E^X|KZ{1|(&N*}D-aBXZ_n)7?0Bqwa!;y4c8Ik2$a*-xn#LDJeC#m7X zG5sb@j=hk|NBd5G(zz=`w7J#qZ*INV?nR=w)J6+`747=|#op<=K>)_kI2RX!8)0L9 zaCWX_9-zX|oU(_7wc^aE0Jj*{Z}=kDhD&E<;eFZ0Ko~QY1PrYkiFW)*c`b~-9`Do` zYQo1wLI-*bZ%4CUXosOHsW4S+cSh5G$G4!}r71VOlq$`&vjGE-k^ z|8K6HI+n2#qJ{em6<=7I+np1J`InlKykZ!(XZN&oQpXxrLsYR&{tny+#Ss3pFKcX& zaaB|>j|GHagvd?$Nh`FzgS&*E5NZ)(6c@T*(fp1FpJ@$j(5MpMpz#o^G?&_Cilw$T e(Z2sep#0YGXd+pmv~$FS-5+?|efAB_iRLemQ`Qdv diff --git a/dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$Is0bPu72.1593101616531.class b/dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$Is0bPu72.1593101616531.class deleted file mode 100644 index 268c2ccf84ce05a9c195252e05cdddf430ca0c78..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 788 zcmbtSO>Yx15Pj2xZnFeP)3iWJp_TTKgu_Nrs|bP+P(^^0s)(R+c3dlR>+G()UR3y1 zAn_q_;0N%d5VP5cT)0%VWRKsQ=b7=?zyJLF1)z^T!dS+kG9qRDpK zVL?AnX78+!$_7WVJl8JHO|XB^>+K&r>URT?U1+0){TRGXyOTFrzvqDnwKMTSghoWu z@OW~jr1MZB)R*krEQ-X$C=b^NJ99pB+JrKWWM*w%#-T8#ka&c~T%zM}tgIGBe=0l` zLPgkVHsb?b!i&*WFEk*OB`4;Lt@dbHZ*dFuZY;Usg;a^tapd7v71vR%U)_Rc`ErQ-P3DrD<$~kv)F(@~E2-9&>S*$WlDvJldd&O>FsS;5MOTvxwJL`-HIi zL`T{^B@A1a3H{%1F=(Guv5lsWGIqG5hw-T*`2QM~)^@p7Wt6arHTWP#+&})7CEnk_ zO~wxxH5d`wwa!=6zvIql-UGWl%gon!-bItwT)W9O*H#wV_g@&~V;lDtk}VFq!i?Yf Nf%~0@-%wv@{sO*)&WQj3 diff --git a/dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$MDfVBZ3E.1593101616527.class b/dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$MDfVBZ3E.1593101616527.class deleted file mode 100644 index 35ca57a202f02af7a78e8c8eb6fa87453d91fc20..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 724 zcmbtS%T60H6g`)u3`qkGA%vFqViqAR98nOeimDbGl|>>XKv}Ret{a&;Gb4|uqW!8? z>O<BO5j3h=Cgr>K-LgGIvIX`2YJ zL|93^bUu_y=`@X9*mY;8AA0SCP-yP-D_BOOj5<~ryDTEi9+}AaH-t`e`d7#ctmXxZ zSmzfxi9c#W`M=?`y3JOLC}0+ID1#WWfBsel-p^x!@g}2XM#Qny`ia_aJg4srfGwUy qPE>imK!ev@T<4gJ=f>h6ywArf*2ankUpvE0x%CGdt=(U!jWrK*Ew^$2 diff --git a/dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$MXBuDuLH.1593101616511.class b/dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$MXBuDuLH.1593101616511.class deleted file mode 100644 index 90cf48d5cb7ef961ad504cea9af559793a99aa5a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 859 zcmbtST~8B16g|_etSyU_4-rH@)WwuyvXwxH(HNx?dDw;oO!ajb$7W!-@3`2JNKS5lXK6`uisbK0Cup(a3tbDYMxpp0-p2&-j$IJR4>uN zUeSJ&^xhjTr19IaI8`>zlfSjSxw*Cde5dL2>`ZCJ&AH$HxSPGr!hIKvp?1Q*@IdpZ z8yp;;NMT(#4AX<|VHQRFSW6eT7?y7OnN>OvaU?Qhav3eIbwP0%W^X7e4vwTzT&u4I zQemiY)6IHxpvkZsjylkbp$nfeOoaoV=q;t>??*&eRP}u71Hsrg5@g8ct{?%;!l#H z`q#p=wn~07~SpSDOehdPsuOFU|9T+rQ5FEH09{s9B3-x&Y^ diff --git a/dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$Mr3Ng1rI.1593101616441.class b/dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$Mr3Ng1rI.1593101616441.class deleted file mode 100644 index 1584ef01c312128f1ef4a0ffa83dae5da711b63c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 724 zcmbtSQBPAb6#fnyTy~u}#{fZ59`3_7A370242Gy=L6c1qG5B0+vlP4brn}dmze*C+ z#0P%>Kgw_}Tig#=llGkN>(`$1HNF1#=P!UgY!ObB7uw2EW|WZWP{@IHnJ~lDitd_6(&1z_$P*z-+D>hrbX}@C^yt_Q*x)(~Pnei^dy(;dYTEQId6JE{QM1*C+ znzG*ML@K36BOj7pIDUK7@1#|%U@1letAtX>9k0#SDPiHIaV9$;beprkLhH1O4Xno~ z;}O5eq5rH2@qfdk+7??aql5)4A_g&H|NN~=oZrO~J!Zk)?T;Q diff --git a/dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$NcHVc13I.1593101616484.class b/dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$NcHVc13I.1593101616484.class deleted file mode 100644 index f84f1e8992dfadf6ce4c8e709d7d77635779e9c8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 910 zcmbtST~8B16g{`CtgVYwK%^oc;+nLTWC}uv!5EbgF)5H(()VGQG=tmOb#~XFze*F) z#0P(XKgxKQHkKzMVUn3UbM84e_nyq}KR4{RABSRR z0Kba+hfXT(g1tz+H))iIV0~k4ZGGd}W-E}{sR@mApMrh0+f}Pi-+CZI^+bM@B9yi# z4vtQ=N<9<_lOyz9X01F5wTByo`Exu=O(;}kRpwmY#0xmlRB$w24%yiLB_v(z3LqxMyy*-e`|T9T>pbsS&nw zs&t$yJ>2ne3w{{|O!*kc?fiC^@bbEoc(_NXX?vXa7D%OZl0+_OyMx^~d##ut>Q@=A z(e+Wo{R(C=M<}|?@>y?m31iQVHR%h&hsz57-+$V=YD3j7O#uQ{F9B9?}%Ic60ZnP~pNa`VX<*Iqfp%#ipOI4tso diff --git a/dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$OKGB6GNl.1593101616448.class b/dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$OKGB6GNl.1593101616448.class deleted file mode 100644 index 9d5b700b285a9550a675c7dd4a63442cd2bcd6aa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1455 zcmbtUT~8B16g@)=3w2clL{U-H725K#wG`1{jM9)2ERqPp=V6)P;C8o}of`C4X<{|; z!580r_5T>}Y%5*VY)EKw_ukIlb3f1Qk6&NE0a(To!?qW?!sn5ep39pJmmi2gyRy;r z-L=l~vu0yQaZe~`GxXj_9UA3SmdfSI(xc^)!&^tvmt4Jbww~8kAJks>78t|uA%DwV zp9cr-*50A;w1o`ANbmGzD+u_WFD&#kOvdLeEq&Ju1Fxl&DWl4LzvE&tjKwZ8bhm|) z+?Ve=Z-y9#xH@Px2vA~Jt)I1_l|;b%RB^pma;K4`Lz&@WN=B`C5OGh-FmQLokzN;k zU#ObUTBu~0MTWVQFcGJHNSfrfF-}4&3~VD&GVSzAqU0(qF{+0qEhWr)G*KF5zL0P^ z?rOBx>DrMenxZF#OD?L_OWTT}jm2Wk5>I6P5l= zYx15Pj2xZnFdkX^r2J$}z`JmV+-`u*c4fE_#_94RNXmE&NPkl9Gcp-zJ^qs)r_ zlzx_tj=facCwor4Gr<)h+3s#_ZFe8-bP}1L8*8NhkQ}Ik*Dt!xL?A-#Oum=G%5*69 zPtUXp5lV#moIT9bRGwNL;R<16!so$QpG9b4WZ?H-z1-_$geLcQVIeb`Ky(y!FMJyo(F=E5`AC`E3 z4c8gpXVhRs9GBW(Q2&P8pLh>!@+>o7<9P>bycXIOj)k@|)4uu4pcw18JCm&PwF}I| O?eDnPe)tvjndT2A8_8n; diff --git a/dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$PzweZZ4J.1593101616473.class b/dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$PzweZZ4J.1593101616473.class deleted file mode 100644 index 194789d07a4da34ae579907bbafa11dbd242ee88..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1028 zcmbtTZEMp|6n;+IEw)DOw)(cM+HFyrDx{W2OeMj_MKXz0< z^qjphyc70cYav~2?FaImii6Zy&CN!mx%p_TZi(bXc}nPy*2}>M_vonEGC+jlv3M^W zPxw9OpnL4f*g%%BI7MG2zAw6-YhaF09^px>JVyq;OtenR*cF~P4lxL&QHTnhj;odM z)L?v)C*+0hC4DB;3D4T+ZD{*c!iR+B83|)8{7A@H1-`S>jd&~LPAL4-DH;T75pt4y z;U=nfdpd&gU^seditU|9H%v1cSTb=DW*!+_HE{`-)75noS5Qn>HwjPA+pB>U!b%hi zJ*wLhQo2zTXsfLcUOeBghXh&up8>S&8Cj!onJD8{0js!8$m+!Bchx>5%s*DXik}b$ zXB9Eqce*`eL4WDkvJXvcV7-7G?y!iv!CRM5_@hx&tgsR}WHFBmD6r3@lJU=#Nd+5u zmWzEBgas@zevfyn%pvy0+8117yTP{n6_rnn0adp2yu`MKb$+Lu@`SVS59iJ2l*7*k etVw8vubX3}RQrazwfjT9^}3%F!ONx`_Io`0Cw?!p(`y{MkJ}0T*Lz|`YKAf9>j*X zNAQby@ZJfjTzFvRN1fU{gxfn?TiZKNcAKHdhT3T1PQvc1x2@s6>G@y`m1FTqa3i8V zKRh~CGWFpx%udjESrmyQqkLRrSU$tER2wdBBs1soF$?&ps)rI!XHQZ!)d_hCyoOQgN`<95)%qc1^N}8I+t<^h|Wk(WFc5Z^T zhf>9MlO+FjSqAC(@`;NU4&YGMiqnm6m?vBKj(hA20YFGrq~t8w qGFHgv1&iZ?;9SAioKD{=*2b(QVtItjG=5;c@$i&-KRU(2nD`gp2K8Y8 diff --git a/dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$T91ZgJXd.1593101616497.class b/dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$T91ZgJXd.1593101616497.class deleted file mode 100644 index 41bbc5dd66a28908cfd5ca584e01ff147bfcc448..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 795 zcmbtST~8B16g@*LOV=v36jTtwWMkU;FqO~{Qe#w7HKqh31dVUQFxd=FcQ(^m1NKxU=YB`{Dce)w|;WjG=ZSJ_>F` z67#*I6D56s5<`8;zR8kA92phh3d8D{&%8EV+C*l~qG7iU8{&|!Esxa@^`3}s1$sbaG=nD$%Tf_4|CT>n(6)N7jrn6Kh0suj%O2E&uf zHW6Tf;eM=qQT4U8=DCpGlFLF$H5yqL4cy+#7rUL5p-YWkSkaE1PwDjJC;XGBSH&Wh zLNsubq2#iJ=4R`VVfK+uw13RdZ(f}BKVC<#by&p;mP3@WO2+QkW5p2u^=(vJC&$Vt zVHR@;!3dF!^g2tlzJ}|B*9bKTF^Y5TFQ|XRtxvQD)@hW9uhF=TWtwyCBE?)=nP}gB bCXjzCxHFL~QQ8?|!uEIEZ9n*m`b6^w6n)X( diff --git a/dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$UeMGnoGm.1593101616501.class b/dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$UeMGnoGm.1593101616501.class deleted file mode 100644 index c1b0722c845ab3fc6c481825c61f58d34f2bafe2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 929 zcmbtTT~E|N6g_vlQg$ak3d~r_Hn_=&#a5 zH1WY7;EyuivKy8sBB9BhJ9Flon{#j5KYxGy2C#)yLQlql3PqY}8HgkbL|>UK&`}Zw zdjohUiQZcwmG!%^{G_wENdCs=`ufJ^%dM6#@>3mZVL$sHRJU#7cI1KxwPSH2f>4-# zuzz%{Wah#lOpVZk+!%2bDi^m1i|2Tr=`fJ7k-4>nja?Cj1By$SIj872=qam3sJ{$u zDujx#{XAkpi}1E{(}uP$61hj88tf?0pCuK zu)R~Ml0xia(!(8i6_haTVH|gh-95saoA%=3KH;fV{lcE@CE~)UFQimy8e6|(_usX< zt%R^vzh1A6Ll5(KP{kY;2#(DSul2?uVeFMQI(tn>uPXU}AK3QwO)X=oiZUMY%h-)S zDnj*Q)@f~pXIqAYG2BL#_oZU={5zD2!E<&%m_UusPxv&?nK+L(zhj1D_6Hu)R|Wu| vqs*669E(`uwGhk=1>PmWw}Q^kGFFDH1!g&nOf-Muar4<3*M5G6*&*>C>5Kbc diff --git a/dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$XOlg3lAd.1593101616521.class b/dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$XOlg3lAd.1593101616521.class deleted file mode 100644 index 8f969833c2ec64b5eed5f28b8d3bbc7542be991f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 787 zcmbtSU279T6g{_z-DZs%)3j=9wZc9m@nJ$gC=wBC2>Or`u+sWEoes;?$?h_lEw#T& z!4L4EKcGKKyxRl=(iiE%%$;-Q?EN^*@l#yauBj4~_w z6M8oreQ;7~7wx6$lksU0qOI+Xjjin$JDo`8XT}=oK1UyiHr}+m#{q~?JCUbSSee9P zcyywb4^Sf1XYBhtN#v2$0d5i2uK3&=D^!}O+_|ESp0su%2?&iViAlx2c1BurKDnt7 zD$>RIh=1r1-VAO!P?u0vT$n31TZ37@lPUOiamMw}l+L_KlK}UsxQ%KBbGT14aJ=Lyf4Nn+JrSy25x@h2r@80fpGJ@bXudU~FmO3x~Nztuh3CkfGct9w* zJmJ0BIv~uyG>P%A2>s^u`TpZobXx~itYRfZ8Eb54FFn?T@ULIv+B!Q`MhWv+KnP;Q z_VKGM@%aw!GJeXa!H766w7;SL0}sFO8Cd64X1>Pj5mtCFv`ZWdZDp!G{K}yCR`GZ$ US?08J%!KWqc+!6U9rdZ^5Bvbm(EtDd diff --git a/dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$XoYwdXNW.1593101616475.class b/dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$XoYwdXNW.1593101616475.class deleted file mode 100644 index 9bb1dbe25540b02820e4a55ee7ca43872dc14d16..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1129 zcmb_b>uM806#gccu8FJ0ny9h1rn)vaWn)Y!5)or5N=Yp=rLBL2$z+LBH@nO1uC2a= zucBZS{NV%mP~zDnH2EP#tP5w(oXdBvvp;`*{{dhP_Zd3EH7%R_ffOe91}5)YPGHJ` zXPX;ib;}#+LEjH#)13lsCV#kUjt*v9P)SE zw7Jtax4Vay2sFeQ=BL`7&~bRzwlpLe7DxIpkhUpYM}$g6Wvp}C9=m7^*^!HM&5orc zx8;YiCB=~9sviyrP-A%3K5IkGnSc)%*yfB}XUMguE$qHg?om|6Eao9YiQ&#pyP3eLL>zBOO2MwGdae?BkPrE-i>2nc~JxnIUVPrrx3$69T#vhy5)6T zLMFN`=(vpe=(fo4^sJN7aEqZTxEF-VYB{|f%X3w5QjEccuzcTDMq6#ac)nHh7+#k) zXL^~=>t6;mJC#nLq|!@^P3s6rqFln53nl}J^IrR%zpi(_9LUsU)i zka$Qe_y9f%F}I1x2gK5Ne9jzu9`c{RzkUPQ!xmwvywFxoqESL-V9Y+u7OeJ=*K0GQTj^$nYtB_vY+a_CACJM5v$3k5X9aM&j_~ zT&tL%L}<*}x4Coj#OefB2%DFD9*q^sJC%n}l(8?Zok|iy^HO5G80ui8HJ_)E3ZWvy zC?E5MF5$&s(S{xq%8COn z@NO7`o=jfq$OF`;sZ@%g59uHrzJ7Jk%?PFT_HhmCXjRd~O~xU2gykp3nfR2@Z!bQO zBEd$Hpp3gboPB?)3Dy6-PwHFTbr~frV+B#9#SbtPR|mDO&vO5;N7#Pi%G`en(@b`3HfVyiEWA diff --git a/dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$ZGNUVBit.1593101616463.class b/dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$ZGNUVBit.1593101616463.class deleted file mode 100644 index 5fb64f5337405a7845dfe21de8fda7ce5850aba1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1461 zcmbtU-%ry}6#gz7jLoVO5S=!ly@6Fd6&tC4W$sh&BkUik< zxaDxSW$o-A2%An53=_lC=Uvz3`;JIqlp!CUcLV8Iw&&VirF0o}?l^sy6vI^LB0XzY zD9Ii9q5me$kmjn@Z4;ovu+}(hL#vU1_o?E>u;hLtQHLtS!u=YbJ~ccS?+4%UucbiZAFTvtzL-~U8gnr4Ns?~gh`ubO2aG^ zBQ8gMO%8iqKeRI71wuSF|%512e&5i9!hv7%zcxy4E{m+hKkjIv? z6-gW&dodhOtM|mBC|%Uxk>cr@OnWk$MLlh>01k z^aL`;AWuK2Jp&dNGf0B2Ub5~r1w-bDihg#6D3wG4V;Dz^er3FybLw5GStM$ zP!l6VO?)Js09_7TCI2P*R{(vgNZ%-Z4&^548In6BCdup~BKZ^L>ZMq+@ChgaAzSDkRPkR4w_Xof>))|gv%#{(T*OH55z(rqK&-EZNygj0K zlfj7-Qn~0bmS44x^AK%rZES3Az1wa@BD>N?3->+hb-#Q*lHX1OFow#7xD?z7+vnZh zg_1r%fuTBKk1}gT&!_;87*>aT=C$E6wlZ@rZ(~mwGm-=hwV_1E{8%|HjQ%molo(3F z^|JvTXfb^1O#7f6hN7gxRI%RZO!^(Yg0c$}u6-p{;&p5T%$M;PF=+v{L5TSl0ucfdu9*Ohl zN~1;Z5Z=w(M1U2- zy4NZD3Z+syO`Q*i{`~agSuY`!Iy;|hSVgOfCe|4HJSHq2nb>5bzc}#s5OwMiUB(lr_+vdP^$bLZU8IlJF~eEkMs4a*FB!gr+0gHQ>FciIkbNiTF% zyW={W1M{O!`mKSSiFX zZQ%kgn$?VjOURhjH4B$9VODbt56?QXByKPif{^R5{!+P3;>)kLZ7zfi0$}8(DI-Q|tyx&W%`|ej#SqtBkKV=7KEwEZ<*D!i!~5b1QBU{1 z)FSl`ukeJJ{~%B#)^EMbPu*`yhV(Hxf=rGsK7lyKaSjF*GhCrxLd-Cw zuZbspO)McD2g4+?biPQt9C9OC$z!h2%E!^frDAL%*rN p_&xt<{OBp5IE3dZ-56PB%U@6~FZL+cQjd1aJ<2wd=wW{7^Ai};S^WS2 diff --git a/dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$dfA2UpPS.1593101616516.class b/dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$dfA2UpPS.1593101616516.class deleted file mode 100644 index 10c2e23fffd8330c135ae1d71920d0d210f1bc69..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 786 zcmbtST~8B16g@*LOV_HU6jTtwxDRc8n5KacQ)5KZ=tCO}1$-WcVKX@Uk=a>;{3=ZR zNPO@I_@j(>+s4L(7sDhocg~qJ_ue^~U%!9+1h9iA3?rFvWklw* z6M8p|4y};N2E#;t&@L%Lu(jRoZf!r`=>#Ib&_)aUDL6gb?H;A?MjjYL?Oc2m+=w{j z`zPm0Iu9j=`iwou<5-*+<>3~?`k2q1He4pL%&jfj*b~M~BpyR!EYS%cDXWFimy?qU zLq*sykLW^&;q~CA59%?LB^9QM&DLPn@8lJ9x-{eZ7gD88C$WcnRoq6kf;lWQyt-)< z9+nuMhRO|-xR$nma*caf%^<4 zo5!>^TgMFZFLbQkONM^)`h5TKDSEBrD%P;-ql|U(b1ykn4E|rcvf3sYRYnQ(Sbz^k zh}@%3S)%*45zEr;A=942k&8}>;&n+;9;syV}J)Z}C z@96A8ipWBiVR;tq#zDZ(d}-ktLwOvJBjtNS2O>5mDWk=Ge*&=>N@Ix9-mx@_`|9)L zCeM)PrXLT8P-A%0o>$O|)Pv6$R@<|ZPtJ}0n50c(>I`csOpCfUY-`?=hEO>{5~y~! zl7dY7LANWjrw=ZL3?roj3oACR!_FgvRU0>OGuf@#xP@Y}yUXx;UWpd&G3*K+MzN92 zpx2e5HqliH4i`cWhuS!8b9DH&Qwte9Hzi=!hg2{vymD%zjQa&_-~mI{!~v~t<&aeS`rP diff --git a/dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$eNVZsKqr.1593101616490.class b/dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$eNVZsKqr.1593101616490.class deleted file mode 100644 index 66b085fe469485851f4b782281bd34cec17745cc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 910 zcmbtSU279T6g@W^yC!aJY-??-{ZLm(Vqs!wDN+%&6a`ZYM)Kg>bR5Fe$?kS`S6Y9S zf>rRrAK;G??>52osaQHNb7#&y=W_31e*gLT3&18G5V|TB+R8LDO2}j&WM4;_FoVR3 zogw@@8N72+X&3Is>Z8fxB82N3YisKpk2l+)%#VyU(jABTP49jB`q23xLhV3)lETWU zFZT8iw90&Vgy}K*Hjg5?Z?%srgvC=l&x{o+j#Tbk(Z;s4c1ZCFv!@gji>`J?TJw3B zsSqmC_45G>+JqOK%MP@4rr;yO>Ny27C!g8@bA&ACB zhHLf$)N#9tc`Oh-mq&aynmxkA6BC*2DdF(Eg8%oQwl3PzGM1_+<1Tx@9e>b->OX7K z+6r4)hKC7UMV0TR!fF2drNU>99tcyY@%ui%>KuvVWa}GddCz^v9s0rm5b`c_a+>!d pmiR0L^CLlUM)0+u^IFErh_%2hkCCa?58P`#JmK1_Czu-%{{nYB^GE;y diff --git a/dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$hiCh2uJq.1593101616488.class b/dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$hiCh2uJq.1593101616488.class deleted file mode 100644 index 60fbb0b729af260a12e6ca9907e2d1353155901a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 931 zcmbtTZBG+H5PqgtIa`ZVK%^os>Y23FWNQV9!5Fn68d5?chVRR|DT~{?bMMX+f0ZVp zi68s{{wU)dt(H&3z$LRY`^+S2z&h>`c2y*_l}T!pka1tgo(@xC`mq(8 zL->#aa$17nSJN5O&V9JI5o4?c)cJ(M4%urln4 zo&7_tQXd{+dW^ow!cgv8?c)+*@dVFOV}*)Bl{uG>(UH~;DL!HLgkmDGtDTY7d>Up- zgpzc!$hjoM4@1f{>I#)sCaaqx!&lGHy-N1*_ko6 zIZ!&z8~C_Z!4*_WC}5_7NnFjN>xAbQUC74`Ld|J)q^u64QaVW@7j)gu>sQ;YnDDrM zjvX6&71VIEjCm{&JeP%h)*E|-iKiws=`+IMtiJ#Eb#0t;vSlolQN(S26P@UtCY1jP zom5v?*djbk;4;b_7joVChbZK#v-d!lLY41#`Br03>?fOFG0Qgh4Y%ks13)bnoR~87b)yJ`^u zmI={J2bSNZQWbe3sgzFB*hd3@@apA$Cn0pX%)jT$=AXqMfqGS}U^PSocL*h)J3gDO zL&D+{<4pFH&~N@D>i^9~uXR|(I@UszvB9$L#V48&{#GrmZLwZul(2{;gdp}Z8L#oahc!M6?h4z2TbXg+eq^Uu>$pFota8`| QM#A=2JZL}qjQWi83zYoP-T(jq diff --git a/dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$l6vxjq2r.1593101616519.class b/dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$l6vxjq2r.1593101616519.class deleted file mode 100644 index 9af7ed83d1b31954e96853ffbdb986723d370909..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 744 zcmbtS+fGwK6kVHE4y{#6DOFK~hx1U%!!!+-7>xl*qYuRlNHuRoQRGgSIHn)4TdM90Qbalpc$4bYki9!#{6+A_ygaxb+4(Bbx!zy9N z=|rd8?#L+pRR+30($1L+o%m8Iog|U-d#?ZG^GQ1<6q?&-6|A9AMjbC0yDTIuelVd) zKN7ml`6rVnSkDs_@rI{#99?Ka`Tr1->K3Qh|#dY?%cxfuW<8!{&@p`Ig@Usidlv@wjXzlz&ZL0YP DE4IKc diff --git a/dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$lFVZWOU4.1593101616486.class b/dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$lFVZWOU4.1593101616486.class deleted file mode 100644 index 7b2387ad8da5ed1b2af774f785efb86f7c47ea98..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 917 zcmbtTZEF)j5PmindnR6OY-{a{eNj(HqPS^EDN+$NME#HwXf=Z0w%a9a-P|5~cT)XT z3Rb}n{s4cJIF|&|PsP%Mnc02jnPKL+yWf9){sORr`wR!ta%Du4YRN@B;G(ahl8BFMIFbb>B99Fow#p_$as$ zQJ;4YkCjY)cnq@>bU%wCacGo}D-5ecJWI9V(nd0KE+3;Mj2TgUhWQ~yTYjLN7Dj&_ zWl9Vs;riKt3K|SAI+tB&_e{f0hDYZ#RL)t)+qrLvIL(}*;-aLfY20e9Gg)>d0cGbV zSo=h(IMp`taU;Mr1SJ$O7hoFK^V3a+XP4c@$1R3s6&(#{426^`NvsPyuKW7cUL$7M ztX^!tS}(v7mdjYg3WMjeh7yM~b2R zPv4}nL3$S9VG37KrgI^en|^^pE;dCE3^S3IbTlkLK>D#1axP5I!!;rdho1UH0xw^;c;k zn)tyV;EyuS(Q5mN7`SA1X5YM-%)Gn%_4~(90Bd+a*i(_vp-fVvgp9jFcC<}}>Bgbh z>cg+%?mH)ycENU}-kUVaU9hscyu7mdY^@c@tY<EF*aow_9-4=`ix>Cv8SDpq50I$ zlnG_&I$4(sT7-@EWfxi>Y517%@nOd|ZQHMgdbk#&JE5ZW3Nzb|DY92sLewM@9^!QaVW@7qs2Zo7dZ|nDDfI zfmIv(KIU+{f?3QHiY~K!)*JhTvFFB`^aY`JUf=)w$ks17*%B5jDB&)@kj>~=6Dt4o zPO3H5w}c|ba1|Af3%UIKUlelj*%v{WK$Y+J`8LO%*pD~AVw!E{8}86&27rLA#K}pv q1uXKJ7t9U{{E>n$Ih~&+)CR11W)&HkXnx1N=EGC&{pb`k1L7YwC;s06 diff --git a/dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$pAZ1ZMCC.1593101616513.class b/dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$pAZ1ZMCC.1593101616513.class deleted file mode 100644 index 5da335b5dcec8aef63ee8caaf0703e313a2f6259..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 935 zcmbtTU279T6g`uTT@$x9wzc-deyA%XQJA!(1gVHI6n#i280kabCgTvMZf2L++0y!} z6s&>|{s4cJc()0rPes~=J9qB6=N|68yZigk&tCvqc*@Y1F;|gDot9iABQA!@IIc%Y z#P`Q=I~jejLMj{fV);qCICo)lcW0-$`=ZqdMRuwqE$msCw2!urdYz6B#!xvHCxS=9 z4Ee#}SV`x@W0;?!?=xe>AW}YVF|1$UnbQ%Mv5}dzc^e%OMPrK3uy{eyG4Cs@MWjEE zGbM(Su)}Ob1r3Hb-J1@ycS&%Yp?QU%v%;i8IvpF{9;6gX*H45YiK3+5sp)F1I~{qv z2+A%@vHer2l3c;Zy#RL*lu*DzfLYwlZ}%Bq-*h4$4;WVSK2L;Gp^#FgX>7x;J$U!F z*GL#z)$2@II}EUb)iRc`#^Bk^&{?e=GR(ZxM!Q!GXIBaT-v_pL-PSg-QAQDu=w)=` zkBXuEZ`WyMi*{RthZ)>PneK&r_4HE|^2L+)z%Yjj#ZM?&A4f{x)`yhQQN~2nQS_l>V5L5%F_g?Sxl40p?ypkt z2l(J0;2$M^cehyZwSnaO&dEu>FNgg8^Ya&gJ{~iSq~pqndC-!JbjHP0*}(NIHGELe zFVfjtFQoFxsgoacaCu02hX)6}!=rvT5!t0STKG@N#qg*%8TYRuFoxQ>_$as$cFNBt z=SqeMC5HNveUn)$CPqcL!LWDDXF(e-ot2sQc^fCfm_iaUG_EDu@saXc7=2ajR2VA4 zPqP^v=rX(RI$|_F8eKBLAzT^Zg4488nm+!?o@FT)e6?I$?$yDCL(Mx zG=m!jukADuQmXmf`DEzNUcWl+rvEor=#Hz{#!id|?lP2oW@&A;#|-OFwbkJn!=Sag zKkac9yJ*HJV~@N#aqksF{LhMcZJ$giql9&AAO<5u&d`S|QGN@z2|ps#AjBwcbiShg z9rr#{4(!t`6JMiwA5B_w?KZ_+TUlt|eIbyKT|8JwcIetQV&cvZJnTIAhWbMD7j`$m AK>z>% diff --git a/dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$qbo7evRl.1593101616507.class b/dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$qbo7evRl.1593101616507.class deleted file mode 100644 index 6fc923779c37b657ad8cf668b4e057acb431fdc3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 724 zcmbtS%T60H6g`)u3`s*tLkKO8lEo}SSU5tUsG_RcqNrU2Apv6V7%MV$#)D@D(SB7c z@sPUU1Nu=_?~sUmKrGqU=iFo8do2I`_vaBn7fr&caza~~q(%uDkAxiRC>3TDThSZS zpX1S)mrDEa$f@rpb$JN)_IG#p_TP6qq0DZKHPZhGuLiEGuTN|MB2+KswG>uHLvcK~ z)G7^7Ak?PpNft$NV0C~Ogv}>DON|xEMJn??Z{tu}JC+25`jf;sajLzM*8Ci2N`#X1 z!)(L{I)pF%Sq&W!ii!(!#nyIz+V9u}XP2j3??&l3H7*LURKZJBN|?iI!lzlA2(V08 zjb)bTP%5R9#QCuAkH39A>coUXYx}%{6*S7IW0kSbBEtL!6PffQq1T%I74iaWd4VD} z_(cxgg(j4r4JXwmTP>o1c`Tp|V#NOWTNQY}h*yl?Fnh295dzi12)?`cc@J?{{a=nxDx;X diff --git a/dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$tlsZFOzv.1593101616506.class b/dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$tlsZFOzv.1593101616506.class deleted file mode 100644 index 7f44abbc0911d72369c00eefe87aafbf2a42328e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 735 zcmbtST~8B16g`(#me#7Jl&UC7%s!OzFb#naqcJL}`cTq@8XtTchDkFxo!!jNHps7% z1U2D-AK;HN-ldKG0iEQ|opbJ)xgVK7f3I%<9AJwuREf}5X2B>S)3J~v?Se4l)QaAe zewU6vd#SXKj}rCGgrsos-u~|H-u}yjZY=Y2V~zCR<6yI|9|jkb2t=r#$%zzJx{(;1 zoM{yzln9MEdz?EbPppn`hp>6e=fPN^5~p(Si!u(SwNpt%Xx>UpB8J){+vvzUr!shPIPILEFs}YLqOh;PS^UxWSc2qm|!5Ath;#BZRm;pcR zpD5{kcnotB^kZg>=ts)OJ%+VQJaanYGBz@^HgBUXqG&|%85S-nI_5oPwTSeWQKrOD z5_XUcsi48|u6x&k_O1!OWN6+X=&UfQkWR;j@Ap#*rRycakVH{Z@6>d))}4$zS_Eb1 zCs^l9sw7wN@i4$Nf)Wat4={rV`Rx(I+q+KW<1xePvC2C=6;6dhN|mOu4ZHU6!~26q z!qBSTX3pACfMq->V+kt^p3MxM)!Grm)N5_Dd&6*klkoq2Vtco3Z3F9N6tP9Gqa7bB zhVs?6)5<1ow+IhYxQ{a33;FWtwYx15Pj36-6o_Ek~GlLLJxaL!@*J1szD$GjiPc$RjI9dcZ?Oec-JfMZdLeI zAn_q_;0N%d5VK9x{D4@p$M4OvXP)K1|9<}gaD+X=g>ph$nPx@_nM6WPb({$kC02}P z^!p^b^ipXbo;vl(WUdI|!C}9DaQNz|7s~wBSR?)CFdyXpW)P_WM5tfO8!4=er{ZjK ztyLDFL})D7^E{5_#OeU6gxxzn&x{qy#VYr{Xk#d?ok;>h>rP^vxX|86Yrf1f6+%V& zX&&)`9^rVrtfAM0vf{#Av9mv3^gDCG+2(>9-71}A#>D|1)$jnd3Rdu#@NU^A0yGKj zG;>KPmC|YId^q-JA5Knt38B>4zp7yq?J8P$!r13AVeO5HP4<>B>MZ{VMS<<2Kp8vy zAVc?26RP)q)A}AeEu(}rtfLBI#P<1Dm3Y5_hm4;y+GIo=E8XvC{KQlG$^dx5v&@M) o&n>ihEyOL3g?N1~{>J-aY~$Hn(dKJcn5lMuVYmD82O4wDe<|6wx&QzG diff --git a/dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$wQexNdSn.1593101616465.class b/dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970$auxiliary$wQexNdSn.1593101616465.class deleted file mode 100644 index 15ecd315a39132e3776c68b416178f3fba3189a9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 790 zcmbtSU279T6g{_z-DZuNrfJpIY76_2qz@BHLy?H6CFnzH(Ukf;oep8@W@ee))#$HM z@B@7C2l%7JyG<~@C_)Ej?%XqH?#JQ&`1$QSfPHKehRO*Y%QQ1e$YdnHkG)jdM~6 z==5BxEI^4+pQA5xYvpOI16(0&Uf_9VVxgQ>x%WjG2QrSQlz`BP(zu?vfz5`pl%Z%4}-^Mzh1$UKg!L7`=uRgLrSABWY-wh~nSd>jk$ScbC0ALwy6^ z!bi|VH1Q7~z=tx<9YjbZm85PmJ3F)A%+7v0`}5cL9{_f+$* zL!&Njt~@!Oc8iE;j+hwmGY)vb!1ftZ77;)cQb+Yo9 zAsfh*zu-E_O_EzAD_>!JB0sP}k|N9^$vqTkj^b8_aSIdTUVo0_Xsn?)gcsrhLFvDt WTwd?fSsQ(_Hv4qiVycgoVa#uX8%f;& diff --git a/dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970.1593101616438.class b/dump/co.elastic.apm.agent.impl.ElasticApmTracer$MockitoMock$452245970.1593101616438.class deleted file mode 100644 index 678be648f6f73b804cdef5300087e1ebff82f960..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 25539 zcmd5k2Y4LS(KBbsdtL+>+ZY#$4FY6gquwl2WXX~(%aW~TONNBg-RgAm>GrBDD+vjK z1VRWMLI?puC?S;4WpGF+$shjw;V1bCX(SVS8S4xTcQ*Fai3p{ zmu}PjdqPQ_FQv70)z!6iO%0U_AVT@W*_xZ=Pz@RUj(n`DHJ)1kWRYpr|{Rw|mt3p^|!H#RAVH$RYloL@| z4l9_-N@6@$HVPbv&^LSAu(z;MUur584*BByN@HD3wIhQK(+ZqGTGY%1%T zYah*P21{TyoT$PoI0@k`bJJ&AC=vA~Nuu#KZPb?vCrJzadavdSOQ|VvGQv_}_iUtn zsi+oj^@YQ}k+6pFpq;Yk#89)d{&riM41< zvff>9*cPwp4pzmtD6m0=GuUpIB$B>(vRBuWG-XE;K0h;vu&#Llf-=eTRnI8G;<8SwSuzC8OdYUUg?1oob8`-A+cDiYO_EYz zE5iEqmIjDp7NKouzWL zspMvj9j`-6QZ(a^nG~=!2g@KQePKiEddr+(jN{C*{?y^4gZiv zWN(^{je*!mHBCEjk*4`pcTKZrYsbd=j&7Pbd|4yUJm9V1)h`DaB3X?E}@VP@bIXf$Njt~CHtKygEY@zwX z`OjSG_l%8>H;hvd`gUp1?{L?khda6l1~-P13cM5HrGKYE=PSgWs7u)Y1uDD?-i>f% zMAxHPac>BR^j*jVIV>B`z4$}1VUa75%-PP9K9okfPppVwP_o;9xr_qay zqDR)4?APP5@ewN8kR^RU@^-nqyoDmeQiV&t2_rpWdqt57%}o@JWQ}LPK?T{>f2^m|7lG;Q@FEVY%NIO{U^n zOC->%aRohHKd0T6GHc1Xe=M%=<(-g1k&2@wSj)i1Mi+w5y$;w2Z2Pd+bW_8`q&7h- z&5YFaes?v!OCQ=B*wwvLfhQ2Ia6!{9Dnh9LQz{&QClM;FDUwCQnXn@}qj%aFs2Zz| z1*0U-r=&cec2}O-ww7M4wrfm*XAxp9$kP&6&0^HN1^GD@J`4Xv`)G#4Sp4Kl2O-XG zoy_u13tw4+FR9f@Xo{kNZa|MySCHO z-Lkn=iw-F8zX*5y(`?5@WeX4ek_s=tml4)F6VBJX0xu#g3u;M<=|)4rR9tNA^=iQo zEz0{48Y%zd4PMGsg4%>m+w^2Qek*K+Ev91%==1B9p&HWN*QD;g?y~OU)svfd?Pyit zn<~7_X;$r_u&=j0kUe;w34f@<58y|%%}WC1C{L^+&Q@xbqzDR~MOdSc#e#l{ zzkVh~_;Ytf*zMmu=&!1wefeLyw2B_kI#!+Pj46mJde|Er*oeg=f$EwPED~&BvAb+&-Gjr4 zt+BX*ii%z~EPFQM;Z)^F1F@k>HV$)_jlQeq~N& z$GAv2DojYnWhSUM1>?c$5?mztr(<+(Zggz-Z|`ic(?bd#t>RK%W=d^6JBM8bmm#P$ zQsaaU!s$)}6$dx-wh5fn6`2Uf8mSD9$I9Wld|I!irKK`a;}0r$f{Nl?U4e6=xlAi~ zBAsWVLCbpNc3ls*Q@XEnc;Q%H*2R&`D8U-1Gc9P2N^ctVRVM4mXit$+IMrRH_cv7y z2RnBK6kMZXIV;`%cupbS6jR$*8`d0a%y9tgs)U>5vdPICnCl`bIiD zyoZdaN{X-AUGY`dRt{{}rnV|rt709CuiDmUgylO8TtPacwmr`5;#t^2 zr&mp5%n}Z53@;)B6Is1DK*~}h%h{Osa6^Gi)24H0^9u)RCu3nUzji6~4tIq<(lgN3 z>yPy-xJAWIHa~e1F+Dm3yC_-LBUEgnQ|fJ6k`6~}bO45qxjF_=RiVnJP=IEf?ULsm z?(#g;Jv!*CCNd<>h z+|8U7M}4V;V{}zL>GzF?h#h$%3tc{u6&ukTw8`GEf<6^Tn4JZoL~kk*5vS%!gLJgt zUQU77c%;6T7#o#x3c4$&j;21{Uo#L#_QnrznH;r~+FpN+#B5%aW{Su6z zrs4$Za^>s_jmqHFoVyTCw$^JzgE36+mmU|*6qSPATJ&gcgp|+Vf5+=6AU(=4i`s)$q2~Rt0Ilzu2pmiYO@Q%Luc_kS~Q<_6A?w z7}Tp%6byY#3g&iq1rx5HoEnc+Q($qYig&S%EaEUQI>9{BD@+tyM3P*lbJ)8#^4c5( zM~e@Je3h{Tsreo`ntRiP`-se$v_E?YUO)L0Ntv_w}{;l0Jk}oW?D3 z6BEvHxQVKU(WVp)_fa|A$J`xmM}4TFeK0hx;C>Y!=i#1c>)=_;DtJI9p0Zqz(39uC z6hItIt-MFPG_^ghPtrsYCt>X7P-6}h&dlTJ&4ZFuO-;;Cr?yaTBjVHUih5&W%lOpD zR*Ea1Rq?YNHmpDN0V+tlfkphPBfsaAn_KZDN$)?32_~JBI3!oJ0mBEtD)Qn&I$GsC z+3JzF#uZMK+h+JhcV$)G8LZkhwxwP{+BO?-ZL&?v!ie9#q~Z(sWy&|i@j0jYQ;S8% z$Hyp&`igYHm)srewzkp1jl(sY6#SZsUuPFAN+k8DBR7#?U5Ji};LDPmZ@J4&sxcLx zY@F~b_#G8rVQ%Krf|HtX%*LYCO%t`1m1;*E8I>p0(z)F(zJQ>5&l%HjOX-QjFWRE}&A33l)FK!>M?iv1$*0Gzc%8WYSPRVczgkWDfYr{ z+P8PMP-|9}f2v&Zcxf{0AKJc&lAt{HOFXLQum#ZWDIr_Sk%)|i70(e~xCWtpb_@vj zrjW$xJhS3C$_tly;W=6|GySDc;3%G>y+HrH;f0@g;Sn#!ym*5b7kaVV3m19uVlTev z#V5UZu1x9LG=JC^*8);$C7x0tanH#GUD_(J-1kZpLh^J1wNUh!h97oYQDz>Bwe z;dfp*%ZrzL@oq1!@ZyiX_^KCP^5Vk?HE)_;mHNb|GW2-*8xAFS1Ad4s)3YAoIP&sd zu@Oe!^00wih(|Hx859_r2bj*Hi@bEU-6}{_S#wm#`($LHAX6Y+oht!H~+*udrx=zOvZSdd`_zdIqa0o5& z(xv*iJxY;aS12%tl7bxjui|yfp8MhBgeIrSnS|0$150~Vlj3Tf)_cR9puCK z2ty`H$X3thUsTat!Gq&K#SY=e_!9=3qR+SpZRNfuZi556;T*^5=z>EJcq)@sls*& zu_^1wmd#f?lu|sIyS|LXXP#2A0fXPc?-}e0GGgN(kR8-dq!C|DTk6z*PKCh+0q@37 zFxl%$NY+vQMK6Jr=D+u2g9?|ydl=zHIo7;v$_52v7-tl!C#^6)%VPruN5fJEquS3g z!c=Tv@I|SNk6ET^bJxiZyjz0b$>W@6;&jM@d_b7QbJSaqfCTq8pnuluGru~9n>?-H6i&IgOKBtfcw4g5(jQIF? zI;^Ho85IGgLiClG6`+6@(DNjHT*?Fdkt6zI62QV0Tx1tl+9eZLO3=iWak!l}fceU= z7m6#r{1I68 z5F9)2NjUxhtQsnxfztg@{uHczz(86DryEE|5eg#}m+yyKNwbLU5Tnky^)AakWdGv?JOAkaZy+$u<$zoY5tBhU-ii}3cP!BXOP)oVN!FWb=l7j;f7M;$3 zp5I|Gufrqfr8~@Y7Io+>ncvx@(b>NI&SJCcY}!G#=f8<;-(`{Qh55a|Ew6V6+0yLp z;3k(ylfINq`u&z3NMf|^8GexKTUSV=s~G8O3zC%`B0#Z>L7v&FiO=)J)7*3H2G3C6!-RnZsn7+-Aqo9S(md5TdTjPM)Mo?d2q`j+V7 zJE8~Tl5!sdsoa?AjyFd9T4t*I0dY42KTZ#p@F+lZ!23l8?^govw^lp~KOOM?l)?Lp zzp-`l#Cw6!+F_w4t(PL44zluEy%`m;1f$Sct;4lBQ1DHS--(&j#J|j zp#)q?O2A_XKLhBATi^{8Q3ft=wt@A z3I?`W8Q4U~6nq<5ej5J9eDua+XXmPK%# z5WxpcbEImT{d|izWzDyEGj+Un20xnV`DvPD4>#vqV-T%!DvQGP`zPvLJ&dHxoEXCPT}mq%flSdWSI z-{T*Q_DmW=sf&S52Sfg7G%us@c|Ms_39rG+i^V}v!ubrlaK2>0vxtBhqnM$%d$^t37n}XT6@0tI z1YgRuiEpB9(tCc<@rymjq~VVh9A=wZEqFZ8yhA4#yk2AnZQ?jp1tVw=&ticR1Ok-UuTW;bOMbfiM%pBD?w;yMY z3YYLLt*3^HdinMQ^A;}Q+f`=YhE^}-hpSB({3_p`Xu=r$J;M)8iOA98e13M4#5?6- zJdba)87;GlKgsPY%pnT>m}7W`HM7Q~1kDUto@S_wZ%;EMFQp8x@w2sQkj;F%&g6L; zNsbtLo*$l`h8g4AiuCOrd|PQyl{$t$@v|z!gxDm7Nmxv%O#m4EnA{KW!|F8QtNE7V z3_}C=@ojDT_ISRnOW*#SZ|mhPvHX3$&2|qVLp%2n@?L1l({K;r7&kDL2D1Tr5e&Iq-6D>=!Dzcc;Dozs=UVc2yCcuVtl$V)WW;|z#sfF;*GA9lCez;ixnenuk z*c)w|+sx)o(3?N6&3HOY>YHuRJ5BVh6k$o`t)0tQig@d$6vu9C+fDo(l($HLB~&FS zqQQGbZsO@P8R)lVV9+G6Gaag1=H(gBkl8*Ax28KN7Bw91VD*gW9J7OS)5%2^g}0g% z&ZAgGvY6FAVz&F!!D&{zX10%}<87;m#?0Yje_U-UMeG#E&M~aD{7utYCR2O_06m)zV5~pV>T3r6$SVGK!M9)Fc)*Q9<&& z-GmyZ;(>(9w(;{#fOn?TNoy|`n9c7_hrHJ2i$r8gX7(Oi8@brjz$FyP*ogR2v;F<) zcsHBvx1`IeR`idW&6zOP+I+jo{T;Tly~}K#hH0a78~xvH_VEeZ5!`DwPs6Ca z2<|r_&b8OU111{}+B@mPX8SOhf%jr#o}o#0)r{v+vx9Cb|41QbJVBIqJToT9{&XqX XDySz!bEa-g=6J6Cob)Nd48Z>b2aPf< diff --git a/dump/co.elastic.apm.agent.impl.TracerConfiguration$MockitoMock$1166487442$auxiliary$GwsRwzTe.1593101616010.class b/dump/co.elastic.apm.agent.impl.TracerConfiguration$MockitoMock$1166487442$auxiliary$GwsRwzTe.1593101616010.class deleted file mode 100644 index efe03ebecfe8899d6391aa54c905119317ac7079..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 766 zcmb`FOK%e~6orqQ&}oJMX_^*DDHLfINxE=bMQNoXHsK+pqKdX^*RC-m2gi;)p1yt+ zNIax2_yPPV#GNK066=g)U!UV|#@9#l`_IiU06Tcja4H>FMr1)tE=EHxQe^|z!;#^~ zUP$Hp&VJHq?nThfChwj6tb;2`vbELgb$55V-R(r=m)dCIuadVH+2O_Yv5LSLY6EdD zxDhtx2WJB%Lxd7Ted-?O)`~Mj^c{w^TR#ulaOtefy)Rnn3uDGe#L&1!wBtw0Yhm>D zc&EZp5kAd_^ui{?oBdf_YLB5T>2Nx=)!v`>JiZC-E=|4Hmr{+2fFeAo;x4Kc%wdt? z)hrr`u*A>|?kIR|(?m$AvdsC!$ll5EEW0bK*nf2XA#Ss6CB&)4c7-f_&j|IeFq$nZ!0ZX*Lhx??Tk!p}) zR2MqmQ2&8PUuX@i(Fd^&m?fe5vc{49l) zZYTz)BdxLsB|?49e#o7Zr&dR}McBRJ^UPSGyi<7yMJYXL?NkyG8aEQ-#YY{CwC3wH zQz2Aj80KR>@PzQXzlf%u5z2}m=U1Dp{=DXCC!AfMbFVIyo)iO$aJP!vs8+CqHNwk9 z6cS;bu%)bbI+050H1#2|syli=$VOFcU^PYqn}kxx9q-N7F=6?+aVC2~c+*_WG+M`1 z>|i@a84q|eJ%6SN@&Crt+8(!DMhVMUK@4KVbKq}Q;`k0$89!vyU_|U!+CNbLg{^NK v1CQ9s%-7iNVVn0tyTQKDR%Y6h?+l8ygZnedCSSY6Ox(W0Zu{|1)MuK1)Y`uj diff --git a/dump/co.elastic.apm.agent.impl.TracerConfiguration$MockitoMock$1166487442$auxiliary$X025iH8m.1593101616023.class b/dump/co.elastic.apm.agent.impl.TracerConfiguration$MockitoMock$1166487442$auxiliary$X025iH8m.1593101616023.class deleted file mode 100644 index 0b32f0f461214a9a03188cb0b6fb906e6e9cc3ee..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 779 zcmb_a%We}f6g_T2rx^l-G%e6lDAFvFuy9+2v{Dh900Jo};-QwUYs|>Ou_KQM6}}3j zJ|q@=03U_8lLR96v1DJL<740JWB>a7<0pW9JYYDMjw>Uwpd}aM5f`blf$P!O@ONHF z<$KP4)M@TT(9R~WocyGND?+ln+wFD^_B)-uMC7O1XyLyk?|1ed=@$p12#ldN6rTk* z!lrz1GE_1|C^6LM?3>(Labi@2D-2r`J`dV(>8#AXFG}eNW2Tabp)rwY$KNWih0$N9 zD;0){@M%7x4Lc0a`-`~LAwyYGa?09l_2)HDZ$hi9bMD!xRO7;+2sf&@ifRQ*xXJKz z5rsrpWoV@;{Pz@tNxm!*QmQO-J~6U8d_4$5GWc)36EFKZ3x`##VLe6zw-`!3w=_0e zM-0o4wbkJX!^`GkOluugv5AcsWo(gwJ@-K|#Q*%sYTM*Y86_-Z1u+;QvWtFWiRRaE zo$!4^4ML3KO8X4;3*7!jb6}f#nfMy@JJ_JH(5_J|w3V6m_&b53ZQ|}svQEb?5fisB Maj*UG9QB#z4;+HbhX4Qo diff --git a/dump/co.elastic.apm.agent.impl.TracerConfiguration$MockitoMock$1166487442$auxiliary$ZWSBqb7R.1593101616026.class b/dump/co.elastic.apm.agent.impl.TracerConfiguration$MockitoMock$1166487442$auxiliary$ZWSBqb7R.1593101616026.class deleted file mode 100644 index e93b198bdedd8239d35b8148d3c80a3191c90742..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 810 zcmb_aO>Yx16dX69+bjXnG%e6lTBJQB>A@652~rUU0!Tcqx1M%dttXMlF169Ze~vy3PF`FLJN*ERp)wMm z1UJIQyf+*vnFc5@R2OVNvsMg^3UGsAcgkm}He5O@Gw<_My26;5Bw(mbCED>5<+U*S zYPM2hC~Lz z@NyZ61lVARVwLulbpA}+cvh20NU4&<`N+uj=v^-zF|;XF=VP9vRLHTjR4bpmxhNt3 zub{nY)<UuV#4MRJZv6(Lv^nC3+H9ok^lez diff --git a/dump/co.elastic.apm.agent.impl.TracerConfiguration$MockitoMock$1166487442.1593101615981.class b/dump/co.elastic.apm.agent.impl.TracerConfiguration$MockitoMock$1166487442.1593101615981.class deleted file mode 100644 index 5629f74fe761acef350a1a39027cbe08476e79dc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3485 zcmb_eZC4XV6n=&mH>OcB^{r^FwrM4?b_EgCASeViilTt{)@moaLs;1Cgx!gX?N925 zTHESrPkZ{gKdPs9c9*b7XipBnVTawB`^>%1ef|5N-~I$JjPDsrmNbRS1LasIuh&do z6`nGkTHQ70d~OMULV62Mwc&H+NH2F=TFZ`-ddcVO@FM^pDujYh-PslSPj_0URhH~H3PTW(> z&u4HFpQdpFpD{e$o?lB&Q0I!6^-E%bH(W*Dn3FStyAgW^K4&--#=j2M*+!kxj=L_e zxPswzE2nQuq2ktle3ICT13UWCP!46L^sI*O>k6@^Uoja{u|x)^(3i#+$ZrUSDBuj7 zrl4Hp!QzCp$-K_Ke3|^VErWFC+=vkkm{b7Pb7$@Q7Fk=)pdSNioW(hYZp)P(>9jFX zVyhT(>w!I?58jd5S9R$tVNbr)uA^#XV0f!U8m?WGeC2Eg7o%LcwDVap9avKXD|5FD zjHGc{$L`^(P#bFabf_j(vFuto2z@){1d3|htxX?C!nzqK>I^kWStflQ2kSk-Y=J&f z7%h$xTI+(cT(N9#`CJC$5f2kP&%?vB1LvI^7itD3)3~O2*c&>pX{v!6!~k(KBP{9L zl#!7c3`4|#j>h&Zj;U*3a3qm&<5IeUd;dY*lDoXJI6&0hj;On{^VB_lFgw0d8J;n4 zH;sFmy0A^RM~8t~O1MBM-MJ}d5{;B$yl@%OO^qyN0O%uyR|yu%)oFZS+V0bn=GsoG;akP3g@P7`Ym`G z&ro5w)P6|a_2qNN7QO*Hg|QUQGwdhzO2TrvFYHLo3>Kq1oD^Qe98#M0{6K^)S@o-^zrWC}`8 zh2siCSJY3^%}PvAyJwxM$5q1@)Xh6cjz$Ig?S+99{r{52#SZAPg=Qe2r&mug;YyV$ zT&YrrE2SHq-Agii!sjZzWl64s)?J0u?{MG;`VX{E3er>WpQ2kBtw#%mpK;_T9P9i8 z$A8C(M}@b@y+h$IeEFl6p+jfzRV=ihRxQ+Bc!$9VvkSy1-J~&dG3JpNvlwAs)|jJ& z83DecliJn`^hdnLvhV9tie#^8+3TS`jXW9k9D=m&Th@2?w8M6Gdh5|1`*zr~YCI)$&B+mq-F5ie$sXTKq}p8X2z z4UT0+cA@!JqP=SLw)6(YY*%)<`8FLw_VmCdYa@l%6?zLVREV}^(+kP%l}O(v<#f;| T$~WVvHJnB>LXtQyA{_h+m+|a= diff --git a/dump/co.elastic.apm.agent.impl.circuitbreaker.CircuitBreakerConfiguration$MockitoMock$1307674726$auxiliary$8Mb9HwGZ.1593101616379.class b/dump/co.elastic.apm.agent.impl.circuitbreaker.CircuitBreakerConfiguration$MockitoMock$1307674726$auxiliary$8Mb9HwGZ.1593101616379.class deleted file mode 100644 index 16768ec1fc0f828b80b0a64510207a31268c3b9f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 938 zcmcIj+invv5It@}mu#03(i^uzN*74N!xktFP{acfqEv~Bh}0qw5{_|KZXG*vc1!73 zfy7Nb@Bw@jVm65gKM+gy?3p<`I^!e%{`2z}fI4n67-1u+c|TDi;=OLfV`-8|b$dDz z%867Z9VhvrbWvjnpA2Ecntc_gjwi~Ruw})eN^BOw+jrOM8};@2osE#E$4V>iK8E*O zod;VVp1tWn{h)0^6INIqPNRfERF_gyHm#Hzlqos#a49kOjnkZ6ajYyr#6gIfl zLyE^RKcFZZwWU*BtCL}+z);{WPP_EO8pGS>1ZUl3$P3a$Vk*_&tR7DbwW!N8O52}7nHu?{106DL=d<^J5Qw|?W-u$nb8+U^umb#>T|}WwSsFS U$|6ywDC5_@Bdp!}iqeSl7mwxxKL7v# diff --git a/dump/co.elastic.apm.agent.impl.circuitbreaker.CircuitBreakerConfiguration$MockitoMock$1307674726$auxiliary$APBxdT8h.1593101616385.class b/dump/co.elastic.apm.agent.impl.circuitbreaker.CircuitBreakerConfiguration$MockitoMock$1307674726$auxiliary$APBxdT8h.1593101616385.class deleted file mode 100644 index 7ec2e42f089dc29db2432a866659d5488d58d1a1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 929 zcmcJO?QRl56o%g^*p)3R($e}>rMfC$%vP<%q{hU8|7vQasV2t6VVJ_S%MKZ48rrMU z#IN|n3-F?hXIUb80c$vN|8fBpXP6F?nz8DgPB8S$Z2BIJX9$h$JOq3RE! zP$(muvTY;zku+f=mD{Oo==eZ&o#9sLIC!bWk+M1o!GlNZ_09T5{o!W7-AF}>o0H)A ztL;%|?@7-CV<;T*V;)94?uNVVLn*9>3`236eeL3yw@Gs9W87_x$TqGl@9o#`m)fK*+a=3b11 z93=PV;X)n@$mcMJiwy5(*q?_*hWlMpGj}n%BEMet8$Taq;-v zeTMm`DpvLx!-vXDovH5Uv4*mbEN+oY4c(Cp{$CS@g)&`#78%UrEPOChpds diff --git a/dump/co.elastic.apm.agent.impl.circuitbreaker.CircuitBreakerConfiguration$MockitoMock$1307674726$auxiliary$BAX1I2Nz.1593101616382.class b/dump/co.elastic.apm.agent.impl.circuitbreaker.CircuitBreakerConfiguration$MockitoMock$1307674726$auxiliary$BAX1I2Nz.1593101616382.class deleted file mode 100644 index ab7129a23253ecfacac70f662da19853ce5d719a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 922 zcmcIj?M@Rx6g@*LYnQ6EQ1A;axMEv>EFjV!7!zrYF#$=a0TN^4beNWb=}t4dOR!I+ ziC^)D58y)??{*uj5713!@0@$jOwPS0^XvDIp8)E3$PftQOU=Vb37_|SKJUsP@>Q>| zeW9#~Rn)eUpGoUCQn;PMh6y^V8(SVJ6L`BuoT1#kCrwl^jEk+iu*W1FH=0b|%>C7DHB$CK6Mr?u=VW8>H&&IQQa0 z$bK?62iNkLK|Y5mTxWPYL46Kp8CJS7dMWm$RoLIMHctTo`yM=4DR3QhlMiDJ&O#caTzWcAu@wrMuzrRaFy@_ zLJJfb#hKb?6u;uecijC*5Lls=rBZ>`J(TI6&`TqFZbU!&lyK==!t#hRPn0RjxV3Na LYLC94IO6;P?H&91 diff --git a/dump/co.elastic.apm.agent.impl.circuitbreaker.CircuitBreakerConfiguration$MockitoMock$1307674726$auxiliary$E9ZzXDgv.1593101616373.class b/dump/co.elastic.apm.agent.impl.circuitbreaker.CircuitBreakerConfiguration$MockitoMock$1307674726$auxiliary$E9ZzXDgv.1593101616373.class deleted file mode 100644 index b6f168af8638ec3faf478d125cf92317a5588a6a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 858 zcmcIiUr$pp96e=|%dS)B7@$KGll!pEha&$L0`bAg8^**XKr|+HwcZr&+NJ9yxKAYs zXySt(fDc9ZUGCx%-`Ax5^_-qI=XcWUn?HX6wDFR`NXu0shF(i927NAKWjxpYLBgeW zGS$B4lsHw6cXIePhaGFa>o|47YipwSR-S5avl6}DSZi;$H{0vmkx0*VqJ{er?eBaV z9lwju0x*Wki8vEH5hmszdM8Ty00oBXg#DNrBYKGnaF1c-j!(TNN7l&Hxy)flBuOp_ z7;1MCZFyHYEfPJ-Pf83W;o`JU7uFcQ989s-U5270Ph_UiJeahSH%O=R6K?-pszH`p zfJbHAN4bOi+LL*X@?EfW;6sEHM;Z zYG`dV4;g0nw9)!hZn{E2|V;5e3X*4j~vJ%9B2K zf%XsZknjsaHA0N)T5tmho&XS)^xY QhzVQ2@x1lw3e~aZ7TgW&S^xk5 diff --git a/dump/co.elastic.apm.agent.impl.circuitbreaker.CircuitBreakerConfiguration$MockitoMock$1307674726$auxiliary$RNW7KT5w.1593101616381.class b/dump/co.elastic.apm.agent.impl.circuitbreaker.CircuitBreakerConfiguration$MockitoMock$1307674726$auxiliary$RNW7KT5w.1593101616381.class deleted file mode 100644 index a759b21328d4da0571cfd0ea42b1b94039ef351b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 922 zcmcIjT~8B16g@*LYnN4Pq2L!3OyWPg>59lN_ch0?MC+FUi{q_6DPXHnAF=$}|8S_D+M8NyKfJag%f$H_+ zKqxCxm2|D-$I=F^96rrq%jg3YrIshkX#b@V$0{*d@E<%{3D?8b@Zq}8(^C~IZa?_j zulB+hJ8SP9FoyCGKjA^lbrfuMkEBQ(6c{Sw>~^X(@5a)>IflhyK1~!UGFqh8W(r$8 zj&q8`FgK(q6Lh3iJXU9UrNmI;HcET+!3x9M%?b9p!B7;WiNw?!o8wmU2B|tb&OJL7 zvY*Y(!6gqf@Jg7%WrjBs)aPK9VL6h?bJ0nx91M02={z*?zR!gqY^*Okduyxwtiw>K zH`*Smn0GOUh5sCDn{?>RRR-sT$En;pV5rvH|2Ca-HauL%4Hwt2NH5w{58vHon0~CZ zN}e#ht54LD#;%7Y)LayChwN#YeaYbdo&KO)qq!GRz%{paT|05v>dFv5mX^KPs}$UE(jN7BThYIk%f zloN@HTTb#*>B9ODJ|DumHAgB+9FLVX!LAjjDz<3}9z9vFZPhkvkGBGz^psZIeG2x^ z-}PUg_pArTP&nadJk;Do;a=-Rir7Pjp*ZgDC&uuWmL4uKtPJ`|tVoeHB5^KNSm#;~ zDIUZ8fTC>JlumK2`ol?%A;(>mwCRU+h7XMidfjHo3erSk%9Y00%FrN9=f=I4Js~^k zxp}yn#|-j0OyL^C`w8mvFvqYN$++H0nz57J?u$`)uZ{^FTYb2zv{v1K3qe!s0@-}C zxxxEQhD^D#lg9!UeavHtVRr1LVNK-WI)it{bt3nU$Z2`!{}V_V+j-o?Egv_qLc6)d z4E8)=n0~5^ik~rjEKf9@%0V7$DEY|ZE+tjBhmyhn7eu#Eq6o6cU>cX^RY&v!AO8BIN1Ml9OG!HT}-X+hxIyIRuN^k>PVP>b*!` zef%(tK!nDL{3L~yE)~zmCt76@Dum{OeVIEa$5uzUMA*CF^USa#?^GT_;cy_WEhQ15 zbs;fc40SNln)C9cMySb<<`cfqBfL3UVy_PgRmGmzOuKWmXr*kBPd66a(=(-~MQ#zU z)^Qp28dh+P@Op{H@Ozt+FU#O%Agp#xG|S(@v|$;#ND6R>)!c} H=3Mg++T7#h diff --git a/dump/co.elastic.apm.agent.impl.circuitbreaker.CircuitBreakerConfiguration$MockitoMock$1307674726$auxiliary$hn0y2lsw.1593101616377.class b/dump/co.elastic.apm.agent.impl.circuitbreaker.CircuitBreakerConfiguration$MockitoMock$1307674726$auxiliary$hn0y2lsw.1593101616377.class deleted file mode 100644 index 42c3e84ed8302e3c03e20289d2f99db4979fc700..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 927 zcmcIjT~8B16g@*LYnN3^f#MfZbk(*#Y(=CG7$0bO!;shn5{!w{VWsliZ3 zLOGGDq~|0*l`iV!a65+`YmQYvbv#klgs-ePRf)|)`1Dz;z1iMquWyDtJy%+BcM-lf zt&4RXfAGK<$|wAcN1B^{wAVY4BJogQs7$i2Q)75fOAl8VRz~?WQKZNkkvf+t>~O7f zipQ`xq9_}6rBht1%e+!zC~?W$sWDEWX?U6|x{&V?Li zd-HH3z#M`SW-!n2W{UbeEHE@w{J)DkqctCeTnHJ*)`hZr@O*<`b{Puw#$kXZRDCRB znPGmSf7@CuIjO(J;GJ=u%DrQTYW?uv!N;Vn0C#ZL$8D@oPnVie=Y59R7s{yQCBwV= zRDEge2UtVRM-dOmqmDh24E|pW;&P3aUqk`3xC$SP5II8Eqd?!+aGmf&Ld%30#kuBZ zRKDWocij6(5O_qVNTo8JRn+L6(U&M@^wOAq@QFb7tl|EcQl-u_l<}M25H_EDL1oPO E1II`H(EtDd diff --git a/dump/co.elastic.apm.agent.impl.circuitbreaker.CircuitBreakerConfiguration$MockitoMock$1307674726$auxiliary$oRvI9CJq.1593101616376.class b/dump/co.elastic.apm.agent.impl.circuitbreaker.CircuitBreakerConfiguration$MockitoMock$1307674726$auxiliary$oRvI9CJq.1593101616376.class deleted file mode 100644 index 5dfd7d89d47e3f2517885903d99a7f1de58e9c54..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 871 zcmcIi&2AGh7#ugD+bjV>nigm&w9+1uaBzXr2o-T)#Q_vlsgc@4g@ogpmD`OSyxywx zsX*dK;=lv&P>64{frtl)CHwP?XRZ0h`|J0Qp8$H;VldKjm5R)3$;EiYMWT%7dNfYC z)K2Ew51kTc%JH~_FG?6&^Ij*p6JA>ry|MC4ds~#~;p6SzZf~deXg3o1g-*3_A0vBk z{_1J``a=N5&^Q(6f~UeHe1CYVqz_PGXwKM!+!!%TRe&oD8xubFnjBdpbLR?&u}ITW z5-_wT5^ecFIW1CsS)SAwYQiP?h(6e6c-x<2uX_wtNuJ0|yVIYwQZ`7Z%QNofg;e7r zw*WWlxQcoW3%JQ}JV$>4mKi#U^8Y>D{=`NW2`N>USr@6n(UTo?;xlcuf6nl(J=cgkhjpxDEkqR?6nAV-6hrtg&8)FWIaX1@ zB9;(>5u$MEPp{Da8m<$*PpCzRQC;eOLGv4Kf1*9GNwZ3PgXSHq(OPI%s21AVRD1N9 aKrz;FcPd$>YZr(KyWerI``{~@Q_UZ!@bSI? diff --git a/dump/co.elastic.apm.agent.impl.circuitbreaker.CircuitBreakerConfiguration$MockitoMock$1307674726$auxiliary$onUvPA51.1593101616386.class b/dump/co.elastic.apm.agent.impl.circuitbreaker.CircuitBreakerConfiguration$MockitoMock$1307674726$auxiliary$onUvPA51.1593101616386.class deleted file mode 100644 index 97b9c4a0f2f5dd5977ad9063909e66cf7fae502c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 929 zcmcJO?M@Rx6o%iSm9M9BNSkauO2gsRuq zp-@&hm9(wo$I^z4R6b8-!$cj`b(SZ}M8S>`$0{+y5Uf33t#8)X>yI`A?oO0e+@1y| zdOdjgY-7y>V<;T)0S`5gy5Vm7NQ%TmhM_pg?zt%9Z7n^VV^|*LU81NX6A5SSu)_w| zIz>E&g%P4m*pgOptcw*Y)U!RfhN5Q=D~+AuFgSYNk@%o{W+XNY(jC?!}3a z{o%QJxRl2n@;S`lGQ+zm_UB=q;bB)Mty++^dNv&k()uEN~q0t8c7edCdv4L#8 z*;waiErv{`+RUSbMIQ@T`p>wgaU+ASGI#^7o!sp(lq$`CXHS`}Jg(!0k84<_9j%K- z`|dN$K2ecMo-%x>Ox2m{ejY0*`^e%BxzsQRlEMFLLR=`*@n@02EY8CRBSoIj1lVuN9^$1jo*UzDKMh%WR&aZaED|z9 P8Nc=oLG8g86vxaTWAXn} diff --git a/dump/co.elastic.apm.agent.impl.circuitbreaker.CircuitBreakerConfiguration$MockitoMock$1307674726.1593101616372.class b/dump/co.elastic.apm.agent.impl.circuitbreaker.CircuitBreakerConfiguration$MockitoMock$1307674726.1593101616372.class deleted file mode 100644 index 06a250c59db025516e9cd5cd078af4a94130908c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5800 zcmcgwiFX^t9sb5~SY;95s030cO`)j77NxRfJC2Y^uq`K%V>_A}(aKA{i-b_#{kyQTe``udxl)mpYzui=@>?^&bS+3$B>qxb*w*1G`u z@o$C;su|S`rDW@BR4L}8N>(e_Q9WNYqN;AGW!+9&nzE=_(P6P265C<3Ft2CJmSXE> zAv|uXi@I&{CEVT9)qkYFw?B470%J&i7z`xrVu+bmHkuclqI$vBtb#&Pl0007XnNJw z(&bEMHOj>pR8_51lCYbht)yAHV$5n*iQYS#N;0s~-DCj%7hwpwG*g_UsugY1VmLnb zLAZ@;cFxSy5J)gwqb-#cqr|YUV{AdGC{aTxWTR)&3z}*tI?pql+!2TH3DX*JI7qmG zl+2i^47pCXSH)9?!#;fdBne_KLNeO$5r%+mPTQ7V$THm6(OL7hc$x^}S_Jo?1vfJs zR24O+Wo8wltcA7dxxQXyZ93emmeiuw&Cp(hwzPSJ)Q$=}5tWakol`kLf^WBtgpV;4 zK8&bo5SC%3yrLVrVy%Wp;^)`SC9@R?A18+m=mp(A#&EG??ElC<(K#E$t@wnDTW}l0 z{X2_-q+Tj2Hu=FyYV%6ju*r8b=9H!w!qyT#$#9dCEj+ZR%SFu^Rt!T)8ydqUzYy7x zK#9g)zfg1Jrh#};wsp#5v!IqOi&vkje2LlsbP+)uMu&{s(YYmbM0iTLgF-l`lybvn zhSb~D(V1dM?MNY>XQy0>5~5TtHZ!gIyg`nqg6Kv}Mi+V*cB_V2AepwNm8%s)cry)# z`HK@K_f^rfY%Mdg!d*wTOG1C6wCm5uO=YPoh`7kCyPB3mb6u0!vrD;Y2?H|j<{^D^ zR1Yn&P;8OfT6~C{Nct9B_(n$);wMHz_Agi-*T%zlr)#ABY@F93yJ} zRcSG;W(tcvK@17=3^&a@=2RsWA0AzjFe2jwH_!Est33-yI7KGYOI!QvNP%Z)#))C- z75-W+^;Tk;?pP3GLaXtnX_YHQ2)0U!!(s;Cy3A6# zO-3GORoN-c(6#v*W&PFc%cpzdSqf=Os7dZ`lA1$<=ekp|i8Tpj85OSPZM=>Z%fd}b zch>bZs79y19`SN4n@*FvR)rpGP19p)`TW}1v= zOY1!)lk)0eA^#VfCja2%&`M_JXimbTG9Igw|0?5X(OR^X1uFPY3i+REn*4Ods7wy_ zbxZh?jAyv~`%0S4?dUI~shbmr~(%X4U6pwBqYD zS`DaFpJ_%8U?0N`n-zbUZjf}@dYiV zd^>=-0GBhGAnvUvn2d5fa3wI1kguKi=<6zs-aj~Mo1mR&jib# z2e1?X73KEol_;z@cS_UpE;~W&!%f^azhUSkQa)oz$q#iL_i)s28TvTtf0P(M3M;&o zwBi!O?L?ujmH0t2ydbyYjVfeYBKbj<1Z0Ee6K8t$d#4XaIN7(zDblSd9}t}g=$X-)&9HE zWN%)l=Cx;9&lVKBY-x1oYav0S-Awey>?v(h}{tW>|8 zl}0>grF!hFG{`zD^%rN2yDRlgXHR1S(QYRm*Wr5l{3q=~#HWSUYa(}C!HqA|H_!>i zuzP-(q+;~!KqT@8_P>sUyWYW}w{gq8kqv~eAo3nQ^_mO16Q6cLduipM-H|Kk6*OBx zQ9+zI&#+5#ze_VAXx`0f9wVB9;QRC@x6M)dr|fXC+vQ0L>By53@P84!1%m{p zh@el-IN~xss0%yBvzo zh7N_kMH0QRj_+K?!BA`HyWZ{s?Y`&jo~GURz1;-ue&FpM3H>PaV{ezFv!8go2Z+N@ z#qJlEk)X3*dS^E6e&y|Qj+kw2__dc=n@VorWk*RmeuV;E#A@yFtJUMzJXi0f;1(&i zf!CeLBlKm@GquNWIsrz<>%PZVs>i?c610~0+rs`{HWHoR`%3X{Rf<1&`BqEkk4{<< U`cJ-6{G}?zUwx1NE|LWQ1#{)Z6qtpqntqEUQ`B8hDm9W!kwp*>v!9gg}iH@~!*J10bd31Pj{3ZZn zC|`$i~0Yh~v(Uu=8 zr$wx9W-A4Tf^bnfq76-k*Mns~uFH^@WP)z5)dvgjvlr3o`ht5gk!qaf6yRP7w^1r! z1@{@2YAC=u!)~N}KQh*-_byICA*D(Z>%yDs!+!teS({@1=XRRtKqtOi!Ui^rsNw-b z&ZUONTK$A!^_e!>KWBJbTdLD;{iK9#Y!#8m4#n8B=Zc~DKc%F+NBQKD!z$KL1S3SD z(I3sx{0{CC-X&Be#Hg+{zM}FS4?oi!*rT2&zD)fQwrI?>8&orGVXi&>LLh6~cs!SE T(y=SV6dOPAr1A6{Ds#nigm&Ez%y6aB!s_k}4IKsEA5|ioc|{$}wi;vazFGFQk1c zkZ3D$-~o6j)NeO|hzNIS@~Hzo0Xv5-fivewfFY}kxq1^MSLE#KlIm?j~X5@ z6yns-Sa0+hmJhVi?m5Go`a(T-8vQahvEieLEef=2KPm?QUtURNhmtCyfMu+}2O~uB z(cfC2`5oLPyiKS^h*4c>{y_B#55LhI*r8q|zC!&GHfYSWYg99BX{H@~Cy=#GJf2C` TY1<`Y{N^>DG@o6fI@8<$+vwT9 diff --git a/dump/co.elastic.apm.agent.impl.context.web.WebConfiguration$MockitoMock$880733899$auxiliary$J5m1M3Gj.1593101616336.class b/dump/co.elastic.apm.agent.impl.context.web.WebConfiguration$MockitoMock$880733899$auxiliary$J5m1M3Gj.1593101616336.class deleted file mode 100644 index e9020479548076338e86fe87e0209df8ce8f9559..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 859 zcmb_a+iuf95Ix(3nz#niv`x7c5-^a22Ny*sse+Io;!@NL7tx}=pv|%k8#dm^UYE3A z1rj&$zz6VAh;b4TJ|HX2j?c{T_{@=i|M~d~Km)fJ-U<^)&10)Xzz6++cV%b;)gS0U zn9$0h4L-_FurE7p6CSE=5^<|c=x-bGL0OX~zu8=G)a%WS4WB1Nr4^5k{bzUkced+K zdJY&vzQ>Pvpn2F0b~`;Otb+_gVOo8egdy)}>EIN@+NhpbHIg8bC`u)@xz=Nf!>~M} zC=XmNj5|4o9FMw5pI%sJ*x8!paxI3eAQANSN_A_heY_EMm!`ExLm>yLPY%v` zSb&$q92Oa7D#*bSLs`YIWBHuh_YdM7-j_ZXLdLO){FCFSo12f{G#E0K>Vbz96kRN1 zm0@uz^r10Ya*}z0!8zhOk-LWs#md3|8%{_q50`M+#YL>q%qR&*llK_r@2ODP`wTBC zGhMJ%-SbdJ$wd~|$^Eu@CmGy-=EnIF*_uTL^EeF`j1YNEzjlV6&)_WK8-!K~G0F?I zFDQJ&`5(CQi6C&3B1^42#Z{E(PU$O@Q+jSf-~UV?J!M>*P>M8pjw){LJN(-1uP977 Fe*rPR;=KR> diff --git a/dump/co.elastic.apm.agent.impl.context.web.WebConfiguration$MockitoMock$880733899$auxiliary$SusFhZBs.1593101616338.class b/dump/co.elastic.apm.agent.impl.context.web.WebConfiguration$MockitoMock$880733899$auxiliary$SusFhZBs.1593101616338.class deleted file mode 100644 index 9682975ed3290a6d5124ad139431064c720813b2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 800 zcmb_a&2AGh7#ugD+bjV>nigp357HizaB!s_k}8M;5rs+t1Qjjff*fPEZZ~%1_0qIY zRV02S4mFBK2BwF&uCaE91Ey3==M` z@oMDxx$5&{)$dyKNynKJURx8ru<}fMo0q8F-f8Xbw)gfTk&Sesg}aCjvvmLD{gX5R zW2l^p&w?ky#QatNR7oG8z)+pBZ!%*H+a%WU>Y(N`!7+&_~`M3^4QIZL|z24}}yiZ<4tIIR)=}4+!o>PEZWn4$O zgazDYn5&@x%M6>b@^4-8%-L+1MnX!Jrq)H57taq4_TRNA<$q?!f%bIjJ7uh3HAD?} z7z!>kG}aqO42zGn(f%>R>-t=cb{a=ztYa-i5gU|Z*M3wC;s21*$~MJQL;;IfLI_5P zGNUhAp!p5lB)mnaMu<^eYJNraJMLc59N4B_B)&rZ9@c2gwJTI}ZE31K{z4#c>$pFa UtkSUy#DvWs*la%hhU!%F7xstMTL1t6 diff --git a/dump/co.elastic.apm.agent.impl.context.web.WebConfiguration$MockitoMock$880733899$auxiliary$X21vquhk.1593101616329.class b/dump/co.elastic.apm.agent.impl.context.web.WebConfiguration$MockitoMock$880733899$auxiliary$X21vquhk.1593101616329.class deleted file mode 100644 index 5a6d1c316559664266df653767ffa1ab67eb427b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 772 zcmb_aUr$pp96diax$HV|i~)k8JluzEJQU?&BN$&6jU=d16X4Bswa!A<&bwY5d@4=+ zllb5V@Ix7YcU#;SSd;eGb9&mIp7hV(U%vsgutRvKywFw-gHb{b`a<@!3&QjVRw(a+ z9tH7Pcg3mhcD(ywdRZ!i@h*Ac)rAQ@FG;(-*V^B29~>ky8yRb4dX=0#dU*LM`*;z7 z2$gerDTS4;Cth{WwF(gmgzAialQ}25R!6u&*dFs)Fk=fUOViv^M_N0PM1W$#U}XndgFNJeexn+U7c}HM@kR!oFd#V<0i@_ zEMSE&S3?n22^-3KrxU4^9u9q)TwgsuI(l~2Dq{`HF=|*R6w=J`SZ|yV79SgD!V|*l z`drN%Hcrae!e)#j?r~Bb|6UW~|3M5ZI~+j~1uS9-F^Cc8!r!pK^IKSEe4kN`5pi8= zena&KHoov2c)-2Le1-cRZ1R|E*SO}|(o}o;l|kONaCa(M=VKR`iJL#M-Q4|->QwU& D^7+gM diff --git a/dump/co.elastic.apm.agent.impl.context.web.WebConfiguration$MockitoMock$880733899$auxiliary$vAfycOnq.1593101616334.class b/dump/co.elastic.apm.agent.impl.context.web.WebConfiguration$MockitoMock$880733899$auxiliary$vAfycOnq.1593101616334.class deleted file mode 100644 index 987a38c7321437e33395bd5789ea44087985a896..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 807 zcmb_a-EPw`7(HIgvW5bs>jrF$4Jk-%x$vZ2v{Mn%RDq~em;kYHhg{=D9;sa%XS91N zkob|f-~o6j#Fw_v7igrS+I7+y* z#;dXCpVWY#szKM9GaYA6cx_Ge+{zEy+q^~X_I~T&pnZ54iEOMBE!-r!I69liWAiZp zW2l^q3&9g%Vtzb0SJDS4FjN=ptIQZNNK}9;3_DXk^LlDQX3pi7x*|ztl7OK$m1xUf zE2l-GFJ~(yhLUh`Hl!E!8D8|4xw#HQQIZMzd%e+Lc%SWvR@WEYld)8zJf{FR%D9Sh z2`jkCuv9|<))^ke%J*VpoqCfhca)!A8VM;?npziKPM-C8Pv5pE?0=r8x%PGHJ7sKO zGeixy7z!>kG}ap@46Bc{(f%>R%lcCNb{Z#TY-1}#5jzxS*S=Q_;s31C${wXuL;{}hzXlNaIg9B8>(~7Umz*iLjV8( diff --git a/dump/co.elastic.apm.agent.impl.context.web.WebConfiguration$MockitoMock$880733899$auxiliary$wMh2jraR.1593101616340.class b/dump/co.elastic.apm.agent.impl.context.web.WebConfiguration$MockitoMock$880733899$auxiliary$wMh2jraR.1593101616340.class deleted file mode 100644 index 797aeacf44fbd1d200fc0f2cc0e891a27f01979f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 810 zcmb_a-EPw`7(HIgvW5bs>jrELhLnr8yYRGKv{Ml`6^Kb4h=I1%7dp(Wa5^@@Q7kXDi}Mn++q*~ukz{Ig$>!TqYP~65T2gv9*r(ED ze-a=30DdUr?*fs;XPUIXp3~Fz^rXN4{QL!=g%=EG(sGrEvDcD|(U6N+8PD}_lyGT{ zR};@KRG*)!e%G3>I!>MN+M4K{mEW|tS&7>1{Z_NtJ~)U(I?;(1ZW>M8wAUOQHUlt* z(m}x%I!%xvo>h7PdoF@Q5MjQbS{{e!{T+ zN*nE8GkmPA)MlrCQp66bA@bOz47>J=VhI05G%oE?ICUrWz)SsYAW2W7rnrRCQ?dcB!S=+(Wg=CwKT_YxJ+~Qf| J30)V6#uSp=L+UXy7jnRI4S8uxwQ z_kG{>gNOo0j~+k!M|r$&-eiGJJV$GLPWtlReZTweesBK#>zCgFbmBXPLETchsX4Bp zt6CwaY8jq)RU=n0Ro%+Fe9l$R@{~HkQ+-x`(#RBT%{8oiY{=554c8J&tgEZFv!kP{ zyITQc*!kZ$DpDUME~(<(UR~#oqhKjRlf!L8 zGsn5@kZ@zkeg<}MDd{Nw;tWxrXHu}#dBL@8hTVhz=CvX2X03D$g9O7>e5R4}!{2sY!UFgD?4hSLl4bHCvfG?y%G_wz}uXu9N`qt+3w znbMjHwlJ*r@<9aeXtBWUKFu_>l*t*+1qH!^1WMHR>&Y6DAL#O==o((h(2KS$N=RA0 zL~V%qhzPc#IgDG-a#?W5093Gzf;6i+**+^x>Mdz*NirlCq>#v@13pIuDiwrkE|t&g zWN|WrHnfM)iVlXQx@qM}rppt@*NP!l4fH;7@sK5aRj_QAr}xbX*HLv+&{;3BI&nNI2H{B%bId!;@47SA4CW8+7Q^ zXL~10`fz?m!FU)GLeT3BXUyRvnw#x)4rw`AE~xPai)xr{R<4t5dQysd_u`3qa7V6f zsAK<>g41D~5uz@y7Kgo-Wpa%wfyNL?$&J@zBhYaoj1kiGEU6N5JAj)sXQ#GZ&78W_UDZn zb73^1M4homH@%rAgjE#()u8I5DW0730K+mNjyJ9{?5Q0N{0XnNhkK~Ydi1MvkT?+vQZzi1kZ;!-ik6>3IAiVwVvA1GoO*gKop^ zK4>|u0<<)K3EL&lMvzhHCGlp==egGBnUFm16g+nkPs#8bx+!eYMIVY2AA6-d{SrGM zu=_oEk`n$c!5h#^V2X%3;6oBTBEW~|gYP0R#dRIvkepJJ@eqlUyy3!EAJD8+Iea6y7?0d|7HE{eXyHG%On)}A9BL}1L~ ztWU4{nBt-w0-G^6k9-mLN^}VKRmcxuiN|p!x*>W$iS^(GJbE7Mqm9wWD!Ws(d%Ut6 zqumpgT_O5Z^y$j3pJ>lic87@PvvT*sc_fJTVufaSqBJ$(rHX5+id@7io(G8hDj89F zu9p0|rzer$2qeE%Ccj-tpIY?0W%7H0=nu-|4+F^`m&u<5l0PexKd+=&?b}~^WK?qs Fe*+hgMqmH{ diff --git a/dump/co.elastic.apm.agent.impl.stacktrace.StacktraceConfiguration$MockitoMock$1707322855$auxiliary$2KyRX7o0.1593101616278.class b/dump/co.elastic.apm.agent.impl.stacktrace.StacktraceConfiguration$MockitoMock$1707322855$auxiliary$2KyRX7o0.1593101616278.class deleted file mode 100644 index 23cf57bb911fdb0228de4c4bdef16650a695d913..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 800 zcmcIiO>Yx15Pfb!w^>37Y1-04X%Bly!og`!6eKDx5hn^r36x$P<3?^BJMwy~w7;rI zd`KMl0sJV`*(Pd!K`hxb@6EF{^W=a3|NI5e#R0>abX*yc1}(XmjJX&o8@L`%3{QiQ z7r_gu_~1V6IeVr@nHNDjn|yHcLI;VoZO){3D~5gst?-SJt_hD&E<=6&8-PZ%?kL=3e%iFSOT zycR}Z&nhK`lJKK!Ocz=VUyheB;~R#eq+Y0rdgFL8!fY_AZY;QWQ>iAoUlAUc@et(_ zR`7&ju!O!MY%pv~rf%Y~p(SCcceRy#2yw!?CHr9z2?o-rCKbqd_XM+HY zp>ip%1W$yC`RVzkl0HCzp*m-KnK9x#Q2}l-Y)|;i>x4^dWaeD%tRs?SDhU{B6N$FG zubdW%zMh_x7)rv$*^qA7V|d$Lz>G%>MM+-BM7`0Sk1!pKPS@t#i?LLrJg)%v%D9bk z2}@XI=r6!mfHj6jto(l;>U8q@G!jy(G_@`o@3-FcuKKSi{(tr-Nq2SXkIGobMu-~j zGZb8AXs8J>F~ z&%F~;@%S}x%RdU(O!SI|8Rf*QLh(?Vyul8?o;$^6uXZvPKE&( zL*qbQz} z4NJJm5HCPifK`TeqWse=Gg?l@^oHh0bIl_mrOI>bqVZnu?a@X2ieclw`iap4YYfeW zq#o39F++wJ>BBit4Ic8XXtW)iuFnnk)#MjPo9b4E8QN=cSJg_Hr~&Psznu}xYGTG<_~Oqp&ZzuT_wCh`!+V|EV*kGOKxq( degBn;a&6(xjIvJ8E)f!Tf8uWU!FM!goPW)v=CJ?( diff --git a/dump/co.elastic.apm.agent.impl.stacktrace.StacktraceConfiguration$MockitoMock$1707322855$auxiliary$Tx0jQIZ4.1593101616275.class b/dump/co.elastic.apm.agent.impl.stacktrace.StacktraceConfiguration$MockitoMock$1707322855$auxiliary$Tx0jQIZ4.1593101616275.class deleted file mode 100644 index d6cd63478bc7d76a2f33c68c98a253e9afccff0a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 826 zcmcIiO>Yx16dX69+bjV>nzoQqDAFF1aBy0Pg4C*)hyzjq(JcjWbB()ly0Ig#w+j3! zkob@|@B{jz5O15Pk+{c_{oagcSMx@`{QLV4fIc2GoJq@7Dl)Gn7vm8Zi87w+(KzLq z7xLUYAr+6W+krJ_I?0{z+M4KqqC`9WZvW}-?(<$R68S`@TDZ^A@uWL^`|{(n z0F0qF6c>W0!X*67=}<`@pu|w0vq!lx;xttOZZK?J@wwM2m)6MKxx(2%r0G-=Ff^_t z+VWUAEmHkudQ)Mj2$$p|`k>43?r;G!?lF`lc_9<6_ThYl>0orbI_F+Yq#74_1-Mhi zO;jsb!d-@V0losPGBmx7z0)R%gp?}FtcxZ)z2l>c`1SvOr@Fl=*03I;fenU|%MG2a z_6fuCzBbw)Fnnk&B*peg6`N>=C}WEf9oSEbA-oPOt8G(MWt6at6@*}fC_j3Va+AW#VhJ@1sd)pq_wTTBa$vXXZiI}kS N3lBR_exN?n`~&q%-xdG> diff --git a/dump/co.elastic.apm.agent.impl.stacktrace.StacktraceConfiguration$MockitoMock$1707322855$auxiliary$X6N173wU.1593101616282.class b/dump/co.elastic.apm.agent.impl.stacktrace.StacktraceConfiguration$MockitoMock$1707322855$auxiliary$X6N173wU.1593101616282.class deleted file mode 100644 index 781768399610a5805f53895917a4de7010c444b6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 892 zcmcJN+iuf95QhJ4LQPy#()7fskWvFlKyuS61gVId02fu#N;R!=yInTcw$5(kLkK(- zNSwq455Pkq#!1xh0$FKyc4j`W=AZGOzrTJ1XyOS&PZ&>zJWiDG_@M9cKz9R_ z4wm2)uz(c?e-3q7SY>z?$iyFT{Wju#8F!R^JsnslcDWETj!oo_>Wz0NL;r{&SE(L4 zSjUEqHEc30&q{9_9ay-{U=4Yg%Htjhs~r9>I3o@m+{HZ`cd$jTMX4UYdB(8#QfZaE zV(3=pGKA`xgKd;;E2H4^XBv!`G*HVTyOZWGt<1Je(pMge*|RuKj>pd-@Hf GDf16$`0vU9 diff --git a/dump/co.elastic.apm.agent.impl.stacktrace.StacktraceConfiguration$MockitoMock$1707322855$auxiliary$jIw4bUWQ.1593101616286.class b/dump/co.elastic.apm.agent.impl.stacktrace.StacktraceConfiguration$MockitoMock$1707322855$auxiliary$jIw4bUWQ.1593101616286.class deleted file mode 100644 index 387b40ed0bf66a20c36f86a071637d9da37edd55..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 890 zcmcIi+invv5It@}muv&1X`6B@q;!EKJh&}HK`P=U;vrI?YSRm^j_a(PI=j)XHw3;4 zByQq?58$H^vq{wO1F>X}&&=`Y%#8p1{q-9_15X)xGUUpL*lNkeaKMGH0?YMaXn1Ue zytI*!ig&KA%`oU`KZ%6ZVc@+D<)yY^YP`M1ZsYm>{>ysZ6UkT`EuxQJzxClo_w@Y8 z0b?li#Yk`?0-qmt`$}2|8HVEAdz=J;=o;nV8pGDapIB|U3Kpv%D;gXb$d8QDoXy|x}`>j^1U9EXuN-m9M+k2>!dGL>p8j}4SutYed5 zbuPITSmmq8!5s!?But_Xdkm#Y>;GD3q=P)};l7Kz*rJ?K5==6mF)Y8*fwr$1-d7ef zg6dfw+bFxpVu!}j3@;Red)4i@P^La-k-;*q!v!Nm&8MHAq5BQoB>b4rCLu<4rS=uY z@3{RF4?YtFp3u%xs6hK6%500_A&{<1QD9XeHhd@XOIExS`A&k<>@)t+3EE!qO=-zwp z`5&}*rL=@=uf6`C1+d>vS!%aG8A1_E$--+6Kgh485hs9r*A?V4B36Uz#+NQ;Ddi;L0O>leBOqL0VGHl~F^NOBh*xfe1 zq%144t{5r#Vq%G_cC`H}!}*#tghov3oTEX)PI5A7#*##pX0N$V84g#_Tgj3Dni2G) z2@f&&Y%^|Knvr7I)z)72w)-?1zyk{k_aktgRAJDKQtWDv zghz>}e$CMA0fzau@hwmrZJ!I^5FYd6ARcG9Q5);SS}v>D6nATwFDQB4Caz}8X|CvQ zY$ZIwu*azmk;?IWmRm!Lt}6+hGu*0_5;YkVtHE&s>yAxiv6@<5u+Tdks>ijQO$~8u)5CtJ z$028)@Qs+;wPT%N?3O zKG_-RUYV6}(T}N;L(cdbH;t5pX@>2Tv?-_c2QY&%KjN6B#zSrnX0y7cIx~5S76#&$ zTx{9E{gA1PaX=YPL>{ej)L+&P!~w7t|7fa*Gu+;T^jVk)SCqot|hF`mXTKbD|VBg}DI>|0d4W$O>a*|r)NsP;J$PI0Rwe2`M6 zrY28mM@8q%Wy_BSP=@;YRo&SGeAvyfvlN0M+JZND-z;jR= z2HK__XbsQ#v5MyzP6}u4uMJLF=CYO)%)IEsX&(|kjQY?^=TQ{1^y`yy_(;qY&0`4#)? z|H6Skaqvob9ih7j|AV8yc%Y|n%mX#kRe&19chTd@YygFtGzpy{PiBiJGwRAbC1ehe zOjqFd^rrCr1pO109`;^)hF$E4z@B&PiMj9}2wsOl0#o+Tr%)XB6d&*uPrBeK0lwsb z<99IkGrcK-Kr~lTdfmm{5V&U?oKmjz3W168DoVKvP6_a$175m=Od;|HOb-_%ODPTD+Si+-vULo3|0Av$u+8*By0DO?bPQMkQm{@vdVm9luv3rkOl-6J{MB xIPs_B4~cY_w{rBy1@tGyd@4Ww%#B1bc}ULZ#N%c;Ul!ziRb;AM&NnV0_#Zvd%vS&a diff --git a/dump/co.elastic.apm.agent.logging.LoggingConfiguration$MockitoMock$2051601129$auxiliary$0zVk9feM.1593101616311.class b/dump/co.elastic.apm.agent.logging.LoggingConfiguration$MockitoMock$2051601129$auxiliary$0zVk9feM.1593101616311.class deleted file mode 100644 index 4fe6f4da7d80cb6ae47d57ab8b42d8cb2dabf71a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 851 zcmb_a+iuf95Ixg`nz#niv`x7cQZSH&2Pc&PRYg1yRYFywN<~Vg^0eJ+%CD`5$% zgm;S)<6w=@&gyq*d(&-yQuf90P8+{Mk zsJSTOF}vTfXNutdGdC&M*w!KnSjKg@AV%yp|I!8C-@r}APZ(`7B91G~FQ|OO?H_pX xi6QWmSCLC)UJp^@cTV5nnA1x$`tfH5`Lm5jGfI^^FLB0geuv+D{uPxO=P$VR;6(rc diff --git a/dump/co.elastic.apm.agent.logging.LoggingConfiguration$MockitoMock$2051601129$auxiliary$QWqZNH2R.1593101616308.class b/dump/co.elastic.apm.agent.logging.LoggingConfiguration$MockitoMock$2051601129$auxiliary$QWqZNH2R.1593101616308.class deleted file mode 100644 index 6769fb4a64edfc6ff40aafeb2029b6a7f7367f1c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 795 zcmb_a&2AGh7@V&O-DU}prfGqeLXq~6gu^D00HGo-5mHqpR1~R^%E>im<S2z*9UVyp=9iMr2;gSe&25B2m`I#wCfi zNj#X=zO(OjlBdFJXQQE$r`o%sM7_@A?vqZp+j|y?{8Ae&(vQ*W(TC$#d%eQ|M5v9$ zg@}#d(+*C?O8NjLLVeC2=GKZ6qXJwbY)|;yYZFUnWuB&moxU(;DhUXUiA1~jjY_pJ z`f9pTAyh<~S4QtvSb5%bF(#=`<}jrSJ&p;%S)-wi;x1`s^U7T6)a(u zu(v2P0oDkuMEUz`G``@V|9YNeI?$QltzsP;AsV<%D5bgO zvDrEzEI-#)`xk^^b8+Fdj;h$gW{5JjImo_yrwHM{oU+;uCsalW%UD4OV#KlW-&x}M z4cuh>fKh`Hab0PDLH!%posxfa^WOgs9_plDmTHORy zwn;oJ;=tL@I?239wX@O4$uBx}d5E?;+ui4#Zg*=Z64{A1TKKO~(vv&A-fJ6x2(_^| z7O@dr?cL#6$uvNTP@kjkGi$}6Q2}lcHZJij)h3qC%FO#*&OjJbPy#~ZlA>MwL3u5V zJ}q`Cgo^MU;%dt zZ?8*CfMvqdM5X*PyUtHUx~q+fgp_JJbv|-?qf>n_J|?tT>{X{075Y`IU^PSo_Xs7Q zS)QA%eZt~PZFTyJFlt^|DsPwnX_J0yzlwFNg(zc#ogKJOiV*%YZd%)9%gQKW5laX` zoUxDGt|eaI!fnnUbJpODI4-r%QU8wn-*^pd@+dQ2gp!DZ?2JF?5O(_W8f=?Tlq`TxZnpX}-IHm!x-!eXIhSgfo8;hH8A~Xauz+R4 z+j)s`utI3)WbcC>zRItPZz%DElqyMV?Ae{cg+2&JghI1*SjHM^E>^KlSe}V|ZLLv) zCEg%7BVjVNdqSu+5C5NTO4=^t7H+$^i4E=DGQ34b)u} zai3l9*<(d;|CyRp>TG8b1uWt+To5Dnn1AI0@2}u0<4s2EjELh>`wObyaQz4Fd}0VZ u;8o;Oh1Xrw`JL0(IOg=ylz#A;LH;ywZ%V0g=LOEV?eFl~kG`Th<@^Qs;@w36 diff --git a/dump/co.elastic.apm.agent.logging.LoggingConfiguration$MockitoMock$2051601129$auxiliary$pTrzOMjn.1593101616310.class b/dump/co.elastic.apm.agent.logging.LoggingConfiguration$MockitoMock$2051601129$auxiliary$pTrzOMjn.1593101616310.class deleted file mode 100644 index 1f876b07a8fa70dc71399a2a76335b7e1907a71c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 784 zcmb_a&2AGh7@V&O-DU|iq-lZvK#}&4gu^D40HGo-5eQX@DpAxPIJvG_Ik--MyEI$S<|g!hcCdAN=HSFtibfP#cO1 zks86f9i9%A3=v9%`kZ~ATPseDig1gtbHnFBn^Zb0bMFf~Jz>mL5)m3V67A9><+U(+ zGF_<g+NeZGsm5dH6L&C}=#$~a|2B_;=a<~CVjUYX8n{m=`P}l@Y#kGp zUumnuYr>$pxWrn=Rcv81Mj1PtUeBE=Li{hBac!4lDWimCtRMz4;;i_WEb;sf?lOMN zsKJQ1uC%|R{v8jlcn<7xFEd}`{t%ly7TR^Lg|;%&o?J61+7`BFk`3N=iJ7?l1CQEI Jzo9yji%>+$CZTarq-_+o z`?2NhnU2y}B-+`aSmS?NK$hO~^|wfM2fG+QYj0-Eekx$n9K9b(xvuVWx;F6bl%`N5bx? zit#W@Sdhk96$mL+ujk^x?R9STQTM8dIZXSgV4jeRQ_E|$c1W1`uB}df5IWV-xl%hU zVi`+5^7zaSwcM#9`0obvN^9JD9yv^45;zy8l@Y(< zvF5bfr_+%XK0tw?I%S8cG5l1^08bb;ANkZP9Sdtj>Re{0$F;kP-z%g1 zKEt3fyS$pmWo)1xqKHk(u4m6AL-;S6ab=rADWZUREFc6UL`l&PS)ln-JR`hCs78oU zU1;5*`hfNCGzYe+7m2S>e}OuUnRbmTzkmG$Ks&x?7*LFes>_zG zDG@n87m?Fy&W`9tI<4i>kzuj+7`YiOT`*-^GjgGXp=34N;7cgh+!j679F4|0Brt~P zx}`~|W{4SPIx;83Mzox*nmJjID5mN>7)dVLYO;_@Ek^j8-HM`GmV{b{dP_AmS)Wu* zi-=tu>}6m>wWJ#V7iI{$G6%UxMa|oW$#80TJ+CEHd)7#m35YXnQRfS?ZZYg^9L~rK zazvNORwK!bs@U_k%TMgy0!mx!;njf8s`F0VU8Dg<0eE?m=eO*R)pSI@>wb!;pu;a>7i zrj7*l0kSW&|L z47;2>;6XcH$g5_Ltm|@8R~fE*Ie$Zh;+6e*x$MXdaPg&JYm}izPAQlsuNXys@$%4e z5dj=QqaTOSv?3@(;7K@2!8t2ivpq(N)T?f68e|yUkU~6z23&~}B2);LbEx>eLiP>@ z5Jk+7X0$NWD!P#)PAe0})rujs9N<0t;(3GnDsPy!n(Dj8T}M?(LVKlzYM<*c+W%cgpFZq3I|r-oWK zb&ENJvdu7gEG+kkh==sfa96yGqc0;^P`{J z;!ekjC3PhXF!;$n)cX20o^RfK%_zBUHWt7+!FPCb_~s|frIAF2qUgLIBaZJhi>UP& zly@vfGA(4@aY1)tbLciNO=df0)P#gdKc+a{?aMiG#xQhM zCRHvm1d?Low3q-cW6Y0fJi@R|v&Lt&d^aCMNKXn$Z-p!+lguWg0X!xYdVF&f%A_-i ztlVNtNcy30h3ehdFw8mG77-Gr{P?#{LluecR}B9JQ%xUNNS(A$XLfVcNw+E;t*yP} zgNz?puFm2AMqI)i6--OD`2n&jaousLyfHec6^(DrRJ4RA{8+@3 zK3riq!Sz{R@w{Lf3tCDwB|PK9DIbP>813v~*oIqG8?N{jQY*R`F3(q{|@SJfA<+gAjYFB!HH4{}t_cIp&EjhIpV zrS4Akscu|L=VZHJsx%>15g8gmQHKUd@X_yI)1M+$w6EgxHBjHcCuC=(%H^!o@tl=9 ziL+9Pc2=4SoRvxf$=E@%w&G6u`3>!YB(I9rE#afLvEv8&4Rk_ky5~pebw=OzhQmK& z&rjG_{X6#mh69(vw-CCG@Sk|#M;G)U9&$n3Y2~2W@NKjTnKe90=q3JU$d$Rrl^GW@ zPjZ=EBvT0dmTqzz9H;-}9T&TrPp`oCbL?4%&!B*RNAN0i6PUt?e)5M`gA)Qg!oe3- zDnn7^g2FD_{Vv;!0yM=z4?Cdgn|QQnIdXXgoGfq($EDWbC@CtC)C4Zeak_)c6)kQc z?}EzQ_zU)2CtbN4=V7|%%N`*w7U2hBtvSAhfGQ-1tO5&fyB@Xw3KUw8`t IN`wmj0d(Z#`v3p{ diff --git a/dump/co.elastic.apm.agent.report.ReporterConfiguration$MockitoMock$261936840$auxiliary$Cc88yyoF.1593101616231.class b/dump/co.elastic.apm.agent.report.ReporterConfiguration$MockitoMock$261936840$auxiliary$Cc88yyoF.1593101616231.class deleted file mode 100644 index a0ddb310f5be2b7c2067b99b340191300c2d54b0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 791 zcmb_aT~AXn7=FqohaIQRF;GDSl5?@mi{eLM2II~A2+0-%wGiw zYT|`Iz#nCN&)IbGeofl9&(o)U-#+cH-#>l=*upx)k+fVTBK2BwF*xTUR>pIu2G)7L zR|Ms{)|~1%bHZzDqP~?MwYPbQo^C#Q@ocm6d?OOsg-*0^!>B7eo#D{F4Zs*`XX3Nq zi7+wWJw8*?2PiSrC+uEkj5tnIfEx@e1)q7HaA}RqoXe}}iX<6H0)|E*(U$Kkr$wT_ zj8-ZP72)FSoHlGQ?DVE|uWg31r2Le)+3HQo9$ki3=O^5o3#kUVMFH+qaTC=FW^k9` z-IQtxFwfA6mH+QOc8ip0B&1YnYF!lX^gq3S{h?1b|1G(2w5LCS`HgW4&0>)4~6i0#^ zVRQcc;84kspu|w0p?lF#jMVS{0(cS?PA7|N3JQ{rZ;H!FM644p2{uqVe-4GW19T&UtKsuj%PBE!>D zswKf9!_8a;YW+z0mwKRF44IHpjYiIA`A+}C?)K|G!y~Ho#H6I^>?I40DNazH7qPad z58SC@3Ck%OxWrKM(bC#%?K8|j)K-Vb487*b(f|M3bXxmWtYRfa8EYhU*S%E?=|3e$ zwRO_4j1uOtfE0{85|(~%iT3Akp8V_NHOOOR7usJ@|BlO_X%DQ^EE8U%c?B!97ThJW h1-CNgzWPE=F;;PPN?E3`bA+VrAGp@O^$qnY=P%YJ;i~`u diff --git a/dump/co.elastic.apm.agent.report.ReporterConfiguration$MockitoMock$261936840$auxiliary$FQIF7r35.1593101616244.class b/dump/co.elastic.apm.agent.report.ReporterConfiguration$MockitoMock$261936840$auxiliary$FQIF7r35.1593101616244.class deleted file mode 100644 index 11b7d39cfbb4a735bb09865d5189c255d2bf6749..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 781 zcmb_a+invv6dX69+bp4kG%e853(`I$;lYJM3o7CzP!uT zH^Qd;#nG{nAwr3vK4A}XYsHaK5iT*T4*5K2!=3w>*+3HQo9yLSRg$eiUOsYX)QG}~iTt>Bm8C+v{ zKBZbBEHJF6Dp2dE%D?s|ON5jv%bZWrZvWHEr*HdY?!Q8ZKK67LcB@##Qj7+!Gn9O8 zX>GO+8D<}AtHTq9UUO=OwGOLT!E%f;R>{1Md#@Pc|K4P^4RWQ75@s=n7>p2EMgOrx z`zx3yyiTY=h*6wtU!eXSH@?yy*q~V^zDDyVmT4`tixdlOWvqR3PM{boxHXn6(X}(g P#O)tgYv1{X`dIT9X=To? diff --git a/dump/co.elastic.apm.agent.report.ReporterConfiguration$MockitoMock$261936840$auxiliary$WscYjDAZ.1593101616241.class b/dump/co.elastic.apm.agent.report.ReporterConfiguration$MockitoMock$261936840$auxiliary$WscYjDAZ.1593101616241.class deleted file mode 100644 index 3361fcc3b571bbbaf295c61fe4890300c4526a7f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 779 zcmb_aT~8B16g@*LOV=u;6cj3g$v(98VeliMfsl}546%&{5x#(YT5i8@lQ+?|^ zKNtk%+SYu~apr{A)bhWQ{hIlGj4nM7}}j5bFVI?>gN^(m@VTr$|X!; zj^Xu$dI>PkP>+@0?dvz{DiTtvG_@{@_q(6o?VWW0Z}QMPZQ`vm7O)tihI@p5j5?M>6tP0qwe7iL2>&^gR@TUqA_|zs3_>tMWEB0z0_Asb zm+&f~8X-n;ru7ZgAGm)_Ij}~vNPLCn5|(MrwF?w;ZE2)E{z@PpbvzhJ7U|k4V#3x> MJZwGwj_OGB7i|H~DF6Tf diff --git a/dump/co.elastic.apm.agent.report.ReporterConfiguration$MockitoMock$261936840$auxiliary$aPkuzhc7.1593101616239.class b/dump/co.elastic.apm.agent.report.ReporterConfiguration$MockitoMock$261936840$auxiliary$aPkuzhc7.1593101616239.class deleted file mode 100644 index 4ea022b7b0435cbb709ae42dca11e0bcba8820ad..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 784 zcmb_a-%nF97(Hc^%dS)A7$6`rll!pEhXNYVKztd-kZh4K0>rmc>$U9C&aT%Wf0ZWw zNO<56;Ey7HcUxTI^P05ZcTP|HeLd;z-SrKCZ9HT6AT3vk=zA@>=v{CTE91FSJ?lI_ z90cW>)_l@&>V((UL~Sd(+S@Edn_C;NUu?a4xgLr1QYTutZ;?3YrdOZkb^yjuIu~CB zPlSp2`?GT;eSjQ8dCVTB#)z{-1-QqsI^a{U6E3ZhsdHI3O_3x+Nx)DUNVMf0<+Moj z)o`c4P!KLoFX+HJ!+vW*@7iI=OR7(WYxUN+?crl+cW%tRy_Bk#SrlNli2EoOFoj19 z?Sv4h7`m3|dS zJR}x;03U_8lSD-9Gm?FMuFu@Zx&Hm<=Pv*caD(BkbX*yc1ueN44Y^2_4cx1d^MMb> zM)|I@?{%7c5wx>O-^mX;xWXiNI=3I)?L54$?f6UOwJ`d0 za#LZb2%qLd`eBPPK0wi(Kj@>Akwt2Zlq(hS`$&wS5Lq#6|xMYvGKSyU@nz(t1V zbE+l6GQ-VO1#10R`B&a#d*ghGkWyut^GUkf|G5A3O`oAdp>n{X~Zyi^slvN75K%Th$1J~NOzM($l`~@4K;A{W@ diff --git a/dump/co.elastic.apm.agent.report.ReporterConfiguration$MockitoMock$261936840$auxiliary$f9wdS49a.1593101616257.class b/dump/co.elastic.apm.agent.report.ReporterConfiguration$MockitoMock$261936840$auxiliary$f9wdS49a.1593101616257.class deleted file mode 100644 index f78d516941acef003d4f70faa2da0e616a1c458c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 828 zcmb_a(M}UV6g@*LOV_Hk6i^VwxDRc87*v8bRTEQ;A!#Ei290mSFl+|5Gi7!b(XY}( zH1WX?@S}`(+s3}|V!|dfcg~r!_ue@(zyJLF1>iC6GQ5|Tt3;$;OD={3E@EXocWP*z z=lxMou4B!ijx#5`wkGOX`9XV|hv?zvgVv+XCmZXL$WC>lh5Hm8wmu&0ZnQ)I#!xvD zCxR!!#QgRCk&-?@fuTBO`nJ-c3g&$ygFF)J76*`L1$W zB>MAsr^HYaF3tw@!#cxu_kzyVW++OkPlfA^?zHXkVQ6=L%Dp_5YM5IT;941%Q7&Ny z*BM@2P%i=I8Scf(zZmMiI?hz;-{^sggp?{xt&8IA-to@XyB@eZ3hVFpD{aV1&q7dcFl(U%^$vcL>!8F^Y4|Z>WC9jW4tY)@T%ouh6)O nWtwyC0>xZgnrPpCC6IqBxHXY1Qra0}!sZX$Zr(pbb)xwT`@i9z diff --git a/dump/co.elastic.apm.agent.report.ReporterConfiguration$MockitoMock$261936840$auxiliary$fml0gMeJ.1593101616227.class b/dump/co.elastic.apm.agent.report.ReporterConfiguration$MockitoMock$261936840$auxiliary$fml0gMeJ.1593101616227.class deleted file mode 100644 index e4384d10360a5a2d0b75ff59947252bf460a744d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 778 zcmb_a-%nF97(Hc^%dS($7@$K0lKZgDht3}X3C5Rc3}%Z45;VS*Qf>-eJG)+k{8gBU zCO-HF_(vJPyDcv9c}?2yJEy1pzMk~=pP#<~>|l%GqjX#uF$`LAG3axVDjT?01Lp%j z7zO1!&YtQt^CD+G$U{iNF{tXX3Nq zM%a|UJw8)1L?|#+r|dyytvEI+!VQM?5uXKZxO7%#-sjzPgfU}D#84YawBv`$Yhm=| zc&Efr5QmuHvpa2j{21C@nsTo$q#EQFMYvPOO_WQR!y?1$ z8TArjiJ>0cVes0fiI7qaht4PIe(%$}mmhln_c+$=m9dPK7&WXi6ntiBZ8VP<=AUb; z!wZIPV`gqOkIGm>Jw_4hc(&sd=R7H$*XAXFp7 zC@!?Vq52(nuP6sLX%>mE(7cB_t+{rYVy-PswC}$X$j2J)Pb4dJ?Hn<2>jxgR9$uq5 G(fkFk+0Ft0 diff --git a/dump/co.elastic.apm.agent.report.ReporterConfiguration$MockitoMock$261936840$auxiliary$oRNIUTuB.1593101616255.class b/dump/co.elastic.apm.agent.report.ReporterConfiguration$MockitoMock$261936840$auxiliary$oRNIUTuB.1593101616255.class deleted file mode 100644 index 05d414a1985096fe2b85f2555bb41fbf2e847a99..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 849 zcmb_a+invv5It@}muweE(>CQ+NMV5_eb{nQ0Tm(9fKa4_Rsjmio9ntO7k3@J>#Fco zAaN59d;lMXm`x(W2gH&+duGm#&m8&ppP#<~+{Z13zA%y0Jk6Ad_~0nwu}rea%7L+2 z)EfqATP8VBv9ml=CJDPnyi=LULwIN7_QSgy5ALmp+?^<`xcv~C-i!9@S8mG#V<;c; zV;*Uq#L@QNp%j^i0z+kz?YSi3ds=!p#jrBWyG-dwm_#^hb2Tll^@!pzEDkBkL@%XP zT&s_xN{OMwZS0QdhjoT_XG(KzG86^fPZ!r4or&zxFjQTb67u)X~;#6jSb^=_x;Y(w_S!pz0nV_gsP84EHlhc z)NUE0B`2*H7`$Vyo!mZPsMh=ck2xl72DpUFJ}zQ~dRmtZJMS{gK2nLw9y4_6Q**qr z8({aT-1tA@Z6Ybb-Fl;4I-Agq8_0igV2`sC>iu zAGq>~AaIj*kxFISS5c!gr!P^=>7_CK&1VAnTE(?7rAnP=DC0N3BW$jHMPCQ+Na+Gec-V4L0TuB;icqCQMMVl#-dy8E4z8VgccZjl z1rj&$zz6VAh}k3}d_Y$6j?c{T_{`b;{paT|08eqBp(AW0HIEY|B0e~Ycu$%na&ll@ z5*?0%bPa2|s+T&RC~Lx&6(3b%vk*SodHDSC&a)@mAx}q2EAGz1)1mE;%-*>N#!&3@ zA&)dSz3AY$FGb=Z$55JO4^w0Kv6db#Fl>zTX`*x_tP!bmnVJUIdP4CSR>u@&qj%CN zuGRUZQeY@>*Go_6z&68vb53*ZGUNr_PZw8f&6(`UFjQTc-U` zW_U9vEgn`FLKU}Z^6nYUKcr71T@rF3WE@);_V!z+Z}*N`47qBp6JQNxAFEhrSf0sz zWv!N+L|$X?hFquepvzFMcK)AlO4oh5XDThece34@QVgraw7H`%Ab?_%5M!LX6^4{R>LpaPYuRf$Z7DohhYEofjzM*S{mIKlqB$l=ByBQQryx diff --git a/dump/co.elastic.apm.agent.report.ReporterConfiguration$MockitoMock$261936840$auxiliary$rjkBB8hS.1593101616263.class b/dump/co.elastic.apm.agent.report.ReporterConfiguration$MockitoMock$261936840$auxiliary$rjkBB8hS.1593101616263.class deleted file mode 100644 index 8526fbbba1d48cd69ca7fecfcb8a3a99380e21dc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 785 zcmb_aQBPAb7(Hc^%dS)A7@!~u@jh(hp@;-A7#|qMkZjRFf`qqH>!st;&fZ>v{3=aU z5+C>h{3zphx9R@CnzUcf`A*yKJL&D8-!}kWVuj&YTCNh2dM&x=opTW@pb7@ z2jyDUeAaR1gxA(Y9V^eYw|R)xH=n};JL1Yiu6uDB39 z5hmvEPr6F_00oBXh~3YO5hsZXaED>3&u3mITv{VD=kjV=B1s04fT7lxXv+_j(<0GV zgG`B`BwU=G(}6XHz4n;;+GZ$9dY>MyH`=4J2hC7+X2iX{l&Y6o6kxiHyC|11fd>rl z##Bpy8HQ)E^1C|CyY49Ov`ix*rAkxlqIj?K<-_h#hYbE#Y~RbaPW^TnvzQA}!#qR5 zWro&zP@k}-YcfPjgob>O%-+usXV~ydPbX*zH4_b23JLMu(HgK^A;>f574;WSld=|9f(pi~#pI6fs#tbD9Lv0|@j(=8O3!^WG znG!=u_%u7E1M3X?oeA~zhM_3weR{aw=#0xAHbdF@F}HUiRWBb=gt;;vqFllh9y7e3 zP%RPW8J47RRwY78)$co>r2E~oPrF~cWh`JeMh%M$1)o{k>y1N(>9^YI@Q$HVpV&+q zhh?l_IYtrBXg+OstQg|^_WPAJYP^U7rZIyUj1WzOzFdLgN0=r2oKTGrBR|u;LG=zx xR}=#;$cn^Q$ev)C_FTI_KG&8;+Ar4x^0k7eBgrCNJ4H;~{Dsx#%Ue`On*U7f#RLEV diff --git a/dump/co.elastic.apm.agent.report.ReporterConfiguration$MockitoMock$261936840$auxiliary$tC0jrLOr.1593101616259.class b/dump/co.elastic.apm.agent.report.ReporterConfiguration$MockitoMock$261936840$auxiliary$tC0jrLOr.1593101616259.class deleted file mode 100644 index 84c80af223a01af9784adcb0916fa415d57f079d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 835 zcmb`F+invv5Qcv@p-Z+)Nz)Uj(v&Wcgo_Oa6;Kfp3JA5@C@7VxiaXbJR}Nb{b-Y#S zQ-Q=uT<`!q6k;}s2oDfT_IPGK+cUrX`_Io`0G{GL!+T*Oneu^CBI5m%h$m9J$jZL4 zF6s`Av@N5LRgziml+j_^h!4t{+=P#|9zK7(_3TMAkQr*Pczv$rXFg$|M!*?+W~IkmX8})quXqzhj;EX%)U@sxt9!aZE70V_XBL8 z>Z6EF(z|7jB!mA?&Oy0KLKacLEH1+bBahUjFI=Gg70i==m%LT7jO<+F3o74m?FVju vA{V$vvq-5j&2?01&GF?iUK-;EpL3SR2JVcJ6+&hx;y1n{Y&`gi%9!~Jhn?Gc diff --git a/dump/co.elastic.apm.agent.report.ReporterConfiguration$MockitoMock$261936840$auxiliary$wXFwQ9X3.1593101616234.class b/dump/co.elastic.apm.agent.report.ReporterConfiguration$MockitoMock$261936840$auxiliary$wXFwQ9X3.1593101616234.class deleted file mode 100644 index 524f528704dd61efbc18b55383174ee492e2529d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 839 zcmb_a+invv5It@}muweE(>CQ+NMH*|c-TTwp(^4fTofr$X)BeeZ?17yE?YY|>o)YO zK;kAI_y9f%F`Gn$4~V7l_{jEM?oyRR!f4^F&AMswBpFdWhQ%R8nP^X1 z#S?Wt$`lw1+{W&NF03)M+Ebcqiy<%QeR{Z7Z%^7D4MW+5N$ll7$X+IqhbsZ*5EL+j zd4|_h>cztXLp7FZhYxm~baGFf%a98p`+Z}>xYhaa=0&%|kgL^O0hUnqv4~}c`ALs0 zoyu5R57!vHQ=T}veZ)|%wf-M&Oxg-?12=tK#|qW7P7f>ZGt53yTBXk!+O?_aT;C5+ zMa4%RcS-Z6c_$hCf0Fi#6%sR#9A>VoWFR4-0lDX diff --git a/dump/co.elastic.apm.agent.report.ReporterConfiguration$MockitoMock$261936840$auxiliary$yAFPWn8m.1593101616236.class b/dump/co.elastic.apm.agent.report.ReporterConfiguration$MockitoMock$261936840$auxiliary$yAFPWn8m.1593101616236.class deleted file mode 100644 index 136b2ec11f3fa9720d832191de30c293cdac582f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 838 zcmb`F+invv5Qcv@p-Z+)3F(PbX$nh8!o`+@N>LFK3J0ais#TFn#hq*1mCMEsUT@X% zR3LE@7d!wDg_unu!UM#TJ)W7*_RKH;{`2z}fJeB)@K%^eraW^>M7-aNcp|lntn3@> zqP>BUwr%v0N^;AcGCGWnc(0r(On86e-s1-wn-AARo}a2zaeEe?J$t$PMsM~!Foxk z6;IX2VWz@R;Wo*8bYPvK)16RXTMT7E@6*GLW_Minuo=qEjbkrPh3pp+dAJy020;Z= zxWw>kLbZ69V^~Y1i>0$lWF76KJ>hAbg`70Yy+&Gq2UeTL~LN-Ou2q1%|4%+37( ztEl@Z;}%KYHt!^Z|4-4ZS|=^bC}A4s;e(M!0@E)p(f$Hv$-hnB5?Mxeru7B2Z@Bye xH$IUItkEn}s!DSOby^F2VT4yk_`&CbrLl^eBV>_~DT?^5?+9CWzoIr`{sPCQ+n!-Yp@UZ2g0xIHxfKa7GMXgj&-dy7b7uOEnb=CB% zK;kAI_y9f%F`Gn$56DX1@tHXupE^E6W;;r-);_oT@ZC;QfA z$?h;n*RtkN^}OSmvL^0W@j+!a5AprYdruy0K7P0nbAPI|;%*S1^}8Yyeh`2$l#loc zPc%2ZWar>WiY!2Zp)$$tdSm#3mI2N&tPJx$Q#ujW2=833rp2`$Q38g=Aw}6_Pddf5 zIvZ6=3?=S*{+JGIFl@J{G}jhGQPBN#alO%=$Q}(t)rCp!<*AVU+@b)NBFrHwVFvRI zuco9Wzyia%N=Kh%Z(ziGXN~o#9>iP-nWomoz3tA&*Dv077z*`9H^LICAr`UBFh5cH z+*&O;>Ab=aoN(>s&LKm!-u-{XF=;EpHCzvI6)V)!c{A+1&oKK`8I?U_XxFFac4I%n z8fqbmxJAyl>^sR2{xdc$*T~W$3Yf)tgkXfoXZo27w7-Chgl`jCCd4StHNT+p4VQo5 y#wUWn9a=>ym1(V_M(>=yL@}qA#`HIz3FOZjZjLEc>O4c4u=yQv^X^wv#+<*Mui&Qu diff --git a/dump/co.elastic.apm.agent.report.ReporterConfiguration$MockitoMock$261936840.1593101616225.class b/dump/co.elastic.apm.agent.report.ReporterConfiguration$MockitoMock$261936840.1593101616225.class deleted file mode 100644 index 3be8206e32a3b90dfaf1c2a8a3ec42b417da9be9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7841 zcmcIo33we<75;CUc3y{9nueB^ww5iFCMYjWnkFG_Aq#=DNtdKe7Z9Axo4hy4yqVWo z(xfV)q9TflBBF>WB8pIjwzQQ^RNU&m?+fl5?)!qAd+)r-OEa&2rM`S$zuWWf{m-0p z?m6e4bB;d!&|?6a*&>F^bSGt4n&+E(O3UX`TE?*blxyT2*H86}#c(?vd)UkbuI8JL zUDN02qo(ihrKVx^%GSo!EvxDkFou;U%uK-yh6cyYq;isL%CvpMwKXfHyN38)YG}eY zhJti@BE>)1uIq;9DVWJnIYx{>!Cm*L{x z6Zu-7;b)z65rZ~{S;p=_vpj|cD|$z?F)d|jb|$rHXvEO{w%T1})06r_O}FD-D!!m# zE-9IIdeU?!ravKnWjH_PyK#~vF%Q)$s&E=Z!gmII*R(SX^H4L%?gZ~Oi$nL&0RbCYF6oT%NbbAxhx^UkF|}0 z=P>L#VHb(_w;C-NH!V|hCu)XsR(+<=Sg+trx|}tpZTjmNdRO%RpFY)AJD9}TI7h`< zIG5q7le^t6)5~i<+1KqdhPA-*=}xvf{f1^q8!9-D;dBuN++_!XJjI`8S=x|gFgy@< z^(Q4zTj|@pqG&60Z~05$n-rgptp~2l(?vnPwqnN$5lNhn6)Kjac8W_#w^MKdxn@@L zvYk$v)SI!QwuhnTq!i-ub9u;7L5kAAWCRs{FA=*vNvuSJih49M%+xK%CYh#&OQ;n? z&7^yG@`D>4epftg8tF^N`PETQQYgum2&d*;tE26%Poh;u%td914Zo$K+30H6s9=qX zwcLFdW(>LV+}G5t zYg(D26Cu zhaXY-7^K*`ci87THrP1$SZe9wRV<8t%>&T&D-sk zoPsM)=*$f_{8nzKpx_Pn{lm?(7tc5`DU zJ4SwGNy&0$k!-uC?;PoB-=)A&k>`@l5oUGA40oH$YnyXo;$FC(ZTP8e{k_Kt=X$-~ z2s~23mnsBhQK5TFPj|E1*rZ^OigB(&RiwgJGiNw~&#;!Xh^+#qW-OveaVip19N85X zx+T5Gu=HH6iM;1UQo$FOMZr!}Gp(SRQuj+#T+0<~eb%Zdcp2q))1y+^9G)03_&s-$ z#v&O|#TuXE8q>Qo%}sQXuatVesw{f$9&<*>u?;)(C8g~Ekklh z5@AYs9bTd0M!cTha?_KY%D{xJXI;m30?#TLVOf8*=QL4@d!y9qO=Zz)Z+@5V>%oMA zH>>wm!gopu-&Ga~hgGQWRZoKc`J5 zK7-GyIEv3DpeJx7fx!fBN?;$uMJFDSl)=Z$G_SqCn!v>gG=&am=$7UhX(@FQ-;|&H zRsy35>`h=n0&W7=C2&UqwOMl0&k;!dyUT~HE&4*;5uOjHk0AJDFVnRCJwO=AK;%zFq|b2 zQSTIYSQq-SQ>X)o*oWnG-0{7_1=y)H6)X%GMsgK&6EyNeH5Nlbf=1A%(d4%Rd;&@1 zOrUBp@6W|bot{`JVT+X-T(MFWC{}vs#7Z*=vC>#Ztkj2zm1=#l(tuN})ZB=bYGbid z1teCg)x=8m2FbFV`{xZjQZ7$kQX?rxUya?F6Q{3+1W{KfEmbaGwNk z;^55!+y~MuM;9;Fk*U%L{i;DWHU{ zu9u)2IOsJ3bmJkspvSD0ipC-NkY5 znW`Km;u0UbM}qFiqNO+pV6hsygF&u!}x`;D{cQW^4j9Pi> z{$nijKNpz)CCY-u%>P!{{(G!q|12o>ujoN2M*pYq?Wf~JW7Me8UsV;OE;IDyA~c&J vwkPvE9juJgfz1kqWC{h;H1o3Ah0myQLb7CGdrq8r+1$c*RV=z%Yy-~#`!2NA diff --git a/dump/java.lang.ClassLoader$ByteBuddyAccessor$fvlmDHgd.1593101615727.class b/dump/java.lang.ClassLoader$ByteBuddyAccessor$fvlmDHgd.1593101615727.class deleted file mode 100644 index 04962cc4f3bcd6409b292a699f9f53ea3391ee45..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1308 zcmchX%Syvg5QhJwwXrqzR;$KaRlK57cP^@ewF?UcMRDcEm~#x#nm`hTK8|nTs)!38 zz=skiX;P06D+szsLgw<#KXWp9dw+Qau!BX0?Hm0;FEw=AEFCs<&#OB+7w%G}>5Ixe z=gkAd5T56_OVAXR#vZCwu}?po$sH1wx7p zsY8bgET9J|`nP}5a`62A+Zd@~Fr~evKj(vgI~5w+g93pO$W6 tI1U@3^aRX!?4Q77=G)3#zGA&HU!84dmg3Y)6_y84?eeeJP diff --git a/dump/net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer$ByteBuddy$ModuleSupport.1593101617400.class b/dump/net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer$ByteBuddy$ModuleSupport.1593101617400.class deleted file mode 100644 index ee9cb026915dcebdb968eee77eed46897d859c5f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2664 zcmeHJOHUgy5FRHXAt40lk-pK2vq*DJ= zdqV1=>bXCv>Nqa~NGPmw%&u0lz4P!lo|(^o|NZ(60Po-_fiH?1r#m#f8~gsyVF6b- zkA?KPcHY}rZ9FMAc#p->l$)2_i;Ymhv1Tgjg}TpmxrP>M)z=U-=18UjXY92yw^ElZirkPPj``(|(YW>bm670NAwpi)r^e&1e06)G}1?%Q)}Ny!5yM+gq_Aed=hdVFBF zorCpTA^O-oLqvCwFF8a-Sc5_VGO$4)TNO%}1Csu}>)y}lS# zb&twX7)m^FW_U1sfZFkJz!Rvsp~r0K6R1vW>jN6N;B9VBLqCC;7KU3eDZVA$M@!ip zNru5?{jSeiv+PW|hX#*4Ek;S4AOwd@$}aQH6R3{MNJwmCy&l&IELia9e}p_ix_)9` z+st^UvyO#rRyx+u1CqBcv=>WeyBH?qd1dc2N&i{Z-(uXhsy~f9zpB3pOU~-Ax;W=d zTlwsEH0lNwWcr0^>uw1bVG~N0RksOb>Y>jG6lPQ(;k~FM0>w9^;om={!8aQ3~aVb%-OY`383I1$c3-Npp0a a=4pX$&?4QWCAvkouVCjVO2Nd&7Qlav+_fJ7 From ec53de6c3b0f30e0938495213d5aac39df650e33 Mon Sep 17 00:00:00 2001 From: Felix Barnsteiner Date: Mon, 29 Jun 2020 10:16:29 +0200 Subject: [PATCH 33/35] Disallow ThreadLocals in instrumentation plugins --- .../apm/agent/bci/HelperClassManager.java | 10 +++- .../apm/agent/bci/InstrumentationTest.java | 50 +++++++++++++++++++ 2 files changed, 59 insertions(+), 1 deletion(-) diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/HelperClassManager.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/HelperClassManager.java index c6ef38138f..8a43e02c56 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/HelperClassManager.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/HelperClassManager.java @@ -311,7 +311,15 @@ public synchronized static ClassLoader getOrCreatePluginClassLoader(@Nullable Cl ClassLoader parent = getPluginClassLoaderParent(targetClassLoader); Map typeDefinitions = getTypeDefinitions(classesToInjectCopy); // child first semantics are important here as the plugin CL contains classes that are also present in the agent CL - ClassLoader pluginClassLoader = new ByteArrayClassLoader.ChildFirst(parent, true, typeDefinitions, ByteArrayClassLoader.PersistenceHandler.MANIFEST); + ClassLoader pluginClassLoader = new ByteArrayClassLoader.ChildFirst(parent, true, typeDefinitions, ByteArrayClassLoader.PersistenceHandler.MANIFEST) { + @Override + protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException { + if (name.equals("java.lang.ThreadLocal")) { + throw new ClassNotFoundException("The usage of ThreadLocals is not allowed in instrumentation plugins. Use GlobalThreadLocal instead."); + } + return super.loadClass(name, resolve); + } + }; injectedClasses.put(classesToInject, new WeakReference<>(pluginClassLoader)); return pluginClassLoader; diff --git a/apm-agent-core/src/test/java/co/elastic/apm/agent/bci/InstrumentationTest.java b/apm-agent-core/src/test/java/co/elastic/apm/agent/bci/InstrumentationTest.java index ccedd5ab75..686340b164 100644 --- a/apm-agent-core/src/test/java/co/elastic/apm/agent/bci/InstrumentationTest.java +++ b/apm-agent-core/src/test/java/co/elastic/apm/agent/bci/InstrumentationTest.java @@ -31,6 +31,7 @@ import co.elastic.apm.agent.configuration.SpyConfiguration; import co.elastic.apm.agent.impl.ElasticApmTracer; import co.elastic.apm.agent.impl.ElasticApmTracerBuilder; +import co.elastic.apm.agent.impl.transaction.AbstractSpan; import co.elastic.apm.agent.impl.transaction.Span; import co.elastic.apm.agent.matcher.WildcardMatcher; import co.elastic.apm.agent.util.GlobalVariables; @@ -436,6 +437,19 @@ void testAdviceWithAgentParameterType() { .isInstanceOf(IllegalStateException.class); } + @Test + void testAdviceUsingThreadLocal() { + ElasticApmAgent.initInstrumentation(tracer, + ByteBuddyAgent.install(), + Collections.singletonList(new UsingThreadLocal())); + assertThat(getSpanFromThreadLocal()).isNull(); + } + + @Nullable + public AbstractSpan getSpanFromThreadLocal() { + return null; + } + @Nullable public ClassLoader getPluginClassLoader() { return null; @@ -931,6 +945,42 @@ public boolean indyPlugin() { } } + public static class UsingThreadLocal extends ElasticApmInstrumentation { + + private static final ThreadLocal> localSpan = new ThreadLocal<>() { + @Override + @Nullable + protected AbstractSpan initialValue() { + return tracer.startRootTransaction(null); + } + }; + + @Advice.OnMethodEnter(inline = false) + public static void onEnter() { + localSpan.get(); + } + + @Override + public ElementMatcher getTypeMatcher() { + return named(InstrumentationTest.class.getName()); + } + + @Override + public ElementMatcher getMethodMatcher() { + return named("getSpanFromThreadLocal"); + } + + @Override + public Collection getInstrumentationGroupNames() { + return Collections.singletonList("test"); + } + + @Override + public boolean indyPlugin() { + return true; + } + } + public static class GetClassLoaderInstrumentation extends ElasticApmInstrumentation { @AssignTo.Return From c4bfd97cd9409cc77b12615100225c3cacdf3132 Mon Sep 17 00:00:00 2001 From: Felix Barnsteiner Date: Tue, 30 Jun 2020 08:38:18 +0200 Subject: [PATCH 34/35] Add no shading to benefits --- .../main/java/co/elastic/apm/agent/bci/IndyBootstrap.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/IndyBootstrap.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/IndyBootstrap.java index 689b55c20e..0e97c10d5c 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/IndyBootstrap.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/IndyBootstrap.java @@ -122,6 +122,11 @@ * Unit testing: * Classes loaded from the bootstrap class loader can be instrumented in unit tests. *
  • + *
  • + * No shading: + * When loading the agent in an isolated class loader, we don't have to shade every dependency anymore. + * This makes packaging, source jar generation, and instrumenting shaded dependencies such as log4j2 much easier. + *
  • * *

    * Challenges: From 4f565651aecf4e6965c22576ba7aa381637d6725 Mon Sep 17 00:00:00 2001 From: Felix Barnsteiner Date: Tue, 30 Jun 2020 08:40:59 +0200 Subject: [PATCH 35/35] Allow trace_methods to instrument core java classes --- .../agent/bci/methodmatching/TraceMethodInstrumentation.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/methodmatching/TraceMethodInstrumentation.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/methodmatching/TraceMethodInstrumentation.java index 0e97cabcf0..9966711ea9 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/methodmatching/TraceMethodInstrumentation.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/methodmatching/TraceMethodInstrumentation.java @@ -111,10 +111,6 @@ public ElementMatcher getTypeMatcher() { .and(not(nameContains("CGLIB"))) .and(not(nameContains("EnhancerBy"))) .and(not(nameContains("$Proxy"))) - .and(not(nameStartsWith("java."))) - .and(not(nameStartsWith("com.sun."))) - .and(not(nameStartsWith("sun."))) - .and(not(nameStartsWith("jdk."))) .and(declaresMethod(matches(methodMatcher.getMethodMatcher()))); }