From 9bbd490288b7330e8b7fe8073f25a68a5575dcd8 Mon Sep 17 00:00:00 2001 From: Nikita Salnikov-Tarnovski Date: Tue, 5 Oct 2021 14:43:21 +0300 Subject: [PATCH] Remove last muzzle generate method from InstrumentationModule (#4281) * Remove last muzzle generate method from InstrumentationModule --- docs/contributing/muzzle.md | 35 +++++----- .../lambda/LambdaInstrumentationModule.java | 1 - .../methods/MethodInstrumentationModule.java | 1 - .../InstrumentationModule.java | 15 ----- .../InstrumentationModuleInstaller.java | 3 +- .../tooling/muzzle/ClassLoaderMatcher.java | 3 +- .../muzzle/InstrumentationModuleMuzzle.java | 26 ++++++++ .../javaagent/tooling/muzzle/Mismatch.java | 4 +- .../muzzle/MuzzleReferencesAccessor.java | 66 ------------------- .../tooling/muzzle/ReferenceMatcher.java | 4 +- .../tooling/muzzle/ReferencesPrinter.java | 5 +- 11 files changed, 52 insertions(+), 111 deletions(-) delete mode 100644 muzzle/src/main/java/io/opentelemetry/javaagent/tooling/muzzle/MuzzleReferencesAccessor.java diff --git a/docs/contributing/muzzle.md b/docs/contributing/muzzle.md index 870c79741b..7428030892 100644 --- a/docs/contributing/muzzle.md +++ b/docs/contributing/muzzle.md @@ -20,8 +20,10 @@ Muzzle has two phases: The compile-time reference collection and code generation process is implemented using a ByteBuddy plugin (called `MuzzleCodeGenerationPlugin`). -For each instrumentation module the ByteBuddy plugin collects symbols referring to both internal and -third party APIs used by the currently processed module's type +For each instrumentation module the ByteBuddy plugin first applies `InstrumentationModuleMuzzle` +interface to it and then proceeds to implement all methods from that interface by generating the +required bytecode. +It collects symbols referring to both internal and third party APIs used by the currently processed module's type instrumentations (`InstrumentationModule#typeInstrumentations()`). The reference collection process starts from advice classes (values of the map returned by the `TypeInstrumentation#transformers()` method) and traverses the class graph until it encounters a @@ -29,29 +31,23 @@ reference to a non-instrumentation class (determined by `InstrumentationClassPre the `InstrumentationModule#isHelperClass(String)` predicate). Aside from references, the collection process also builds a graph of dependencies between internal instrumentation helper classes - this dependency graph is later used to construct a list of helper classes that will be -injected to the application classloader (`InstrumentationModule#getMuzzleHelperClassNames()`). -Muzzle also automatically generates the `InstrumentationModule#registerMuzzleVirtualFields()` -method. +injected to the application classloader (`InstrumentationModuleMuzzle#getMuzzleHelperClassNames()`). +Muzzle also automatically generates the `InstrumentationModuleMuzzle#registerMuzzleVirtualFields()` +method. All collected references are then used to generate an `InstrumentationModuleMuzzle#getMuzzleReferences` method. -If you extend any of these `getMuzzle...()` methods in your `InstrumentationModule`, the muzzle -compile plugin will not override your code: muzzle will only override those methods that do not have -a custom implementation. +If your `InstrumentationModule` subclass defines a method with exact same signature as a method +from `InstrumentationModuleMuzzle`, the muzzle compile plugin will not override your code: +muzzle will only generate those methods that do not have a custom implementation. -All collected references are then used to create a `ReferenceMatcher` instance. This matcher -is stored in the instrumentation module class in the method `InstrumentationModule#getMuzzleReferenceMatcher()` -and is shared between all type instrumentations. The bytecode of this method (basically an array of -`Reference` builder calls) and the `getMuzzleHelperClassNames()` is generated automatically by the -ByteBuddy plugin using an ASM code visitor. - -The source code of the compile-time plugin is located in the `javaagent-tooling` module, -package `io.opentelemetry.javaagent.muzzle.generation.collector`. +The source code of the compile-time plugin is located in the `muzzle` module, +package `io.opentelemetry.javaagent.tooling.muzzle.generation`. ### Runtime reference matching The runtime reference matching process is implemented as a ByteBuddy matcher in `InstrumentationModule`. -`MuzzleMatcher` uses the `getMuzzleReferenceMatcher()` method generated during the compilation phase +`MuzzleMatcher` uses the `InstrumentationModuleMuzzle` methods generated during the compilation phase to verify that the class loader of the instrumented type has all necessary symbols (classes, -methods, fields). If the `ReferenceMatcher` finds any mismatch between collected references and the +methods, fields). If this matcher finds any mismatch between collected references and the actual application classpath types the whole instrumentation is discarded. It is worth noting that because the muzzle check is expensive, it is only performed after a match @@ -59,8 +55,7 @@ has been made by the `InstrumentationModule#classLoaderMatcher()` and `TypeInstr matchers. The result of muzzle matcher is cached per classloader, so that it is only executed once for the whole instrumentation module. -The source code of the runtime muzzle matcher is located in the `javaagent-tooling` module, -in the class `Instrumenter.Default` and under the package `io.opentelemetry.javaagent.tooling.muzzle`. +The source code of the runtime muzzle matcher is located in the `muzzle` module. ## Muzzle gradle plugin diff --git a/instrumentation/internal/internal-lambda/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/lambda/LambdaInstrumentationModule.java b/instrumentation/internal/internal-lambda/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/lambda/LambdaInstrumentationModule.java index 0d9bb8998b..4594b0092a 100644 --- a/instrumentation/internal/internal-lambda/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/lambda/LambdaInstrumentationModule.java +++ b/instrumentation/internal/internal-lambda/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/lambda/LambdaInstrumentationModule.java @@ -24,7 +24,6 @@ public class LambdaInstrumentationModule extends InstrumentationModule { return true; } - @Override public List getMuzzleHelperClassNames() { // this instrumentation uses ASM not ByteBuddy so muzzle doesn't automatically add helper // classes diff --git a/instrumentation/methods/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/methods/MethodInstrumentationModule.java b/instrumentation/methods/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/methods/MethodInstrumentationModule.java index 3f7ff54390..58abe38faa 100644 --- a/instrumentation/methods/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/methods/MethodInstrumentationModule.java +++ b/instrumentation/methods/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/methods/MethodInstrumentationModule.java @@ -49,7 +49,6 @@ public class MethodInstrumentationModule extends InstrumentationModule { // the default configuration has empty "otel.instrumentation.methods.include", and so doesn't // generate any TypeInstrumentation for muzzle to analyze - @Override public List getMuzzleHelperClassNames() { return typeInstrumentations.isEmpty() ? emptyList() diff --git a/javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/extension/instrumentation/InstrumentationModule.java b/javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/extension/instrumentation/InstrumentationModule.java index 09dde53509..08c71b365b 100644 --- a/javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/extension/instrumentation/InstrumentationModule.java +++ b/javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/extension/instrumentation/InstrumentationModule.java @@ -11,7 +11,6 @@ import static net.bytebuddy.matcher.ElementMatchers.any; import io.opentelemetry.instrumentation.api.config.Config; import io.opentelemetry.javaagent.extension.Ordered; import java.util.ArrayList; -import java.util.Collections; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; @@ -135,18 +134,4 @@ public abstract class InstrumentationModule implements Ordered { /** Returns a list of all individual type instrumentation in this module. */ public abstract List typeInstrumentations(); - - /** - * Returns a list of instrumentation helper classes, automatically detected by muzzle during - * compilation. Those helpers will be injected into the application classloader. - * - *

The actual implementation of this method is generated automatically during compilation by - * the {@code io.opentelemetry.instrumentation.javaagent-codegen} Gradle plugin. - * - *

This method is generated automatically: if you override it, the muzzle compile plugin - * will not generate a new implementation, it will leave the existing one. - */ - public List getMuzzleHelperClassNames() { - return Collections.emptyList(); - } } diff --git a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/instrumentation/InstrumentationModuleInstaller.java b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/instrumentation/InstrumentationModuleInstaller.java index ad11f33554..debbad94d6 100644 --- a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/instrumentation/InstrumentationModuleInstaller.java +++ b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/instrumentation/InstrumentationModuleInstaller.java @@ -59,7 +59,8 @@ public final class InstrumentationModuleInstaller { logger.debug("Instrumentation {} is disabled", instrumentationModule.instrumentationName()); return parentAgentBuilder; } - List helperClassNames = instrumentationModule.getMuzzleHelperClassNames(); + List helperClassNames = + InstrumentationModuleMuzzle.getMuzzleHelperClassNames(instrumentationModule); HelperResourceBuilderImpl helperResourceBuilder = new HelperResourceBuilderImpl(); instrumentationModule.registerHelperResources(helperResourceBuilder); List typeInstrumentations = instrumentationModule.typeInstrumentations(); diff --git a/muzzle/src/main/java/io/opentelemetry/javaagent/tooling/muzzle/ClassLoaderMatcher.java b/muzzle/src/main/java/io/opentelemetry/javaagent/tooling/muzzle/ClassLoaderMatcher.java index c29e6045d5..059e1c170a 100644 --- a/muzzle/src/main/java/io/opentelemetry/javaagent/tooling/muzzle/ClassLoaderMatcher.java +++ b/muzzle/src/main/java/io/opentelemetry/javaagent/tooling/muzzle/ClassLoaderMatcher.java @@ -77,7 +77,8 @@ public class ClassLoaderMatcher { List mismatches) { try { // verify helper injector works - List allHelperClasses = instrumentationModule.getMuzzleHelperClassNames(); + List allHelperClasses = + InstrumentationModuleMuzzle.getMuzzleHelperClassNames(instrumentationModule); HelperResourceBuilderImpl helperResourceBuilder = new HelperResourceBuilderImpl(); instrumentationModule.registerHelperResources(helperResourceBuilder); if (!allHelperClasses.isEmpty()) { diff --git a/muzzle/src/main/java/io/opentelemetry/javaagent/tooling/muzzle/InstrumentationModuleMuzzle.java b/muzzle/src/main/java/io/opentelemetry/javaagent/tooling/muzzle/InstrumentationModuleMuzzle.java index df855bd7f6..5509fdc448 100644 --- a/muzzle/src/main/java/io/opentelemetry/javaagent/tooling/muzzle/InstrumentationModuleMuzzle.java +++ b/muzzle/src/main/java/io/opentelemetry/javaagent/tooling/muzzle/InstrumentationModuleMuzzle.java @@ -7,6 +7,8 @@ package io.opentelemetry.javaagent.tooling.muzzle; import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; import io.opentelemetry.javaagent.tooling.muzzle.references.ClassRef; +import java.util.Collections; +import java.util.List; import java.util.Map; /** @@ -22,10 +24,34 @@ public interface InstrumentationModuleMuzzle { */ Map getMuzzleReferences(); + /** See {@link #getMuzzleReferences()}. */ + static Map getMuzzleReferences(InstrumentationModule module) { + if (module instanceof InstrumentationModuleMuzzle) { + return ((InstrumentationModuleMuzzle) module).getMuzzleReferences(); + } else { + return Collections.emptyMap(); + } + } + /** * Builds the associations between instrumented library classes and instrumentation context * classes. Keys (and their subclasses) will be associated with a context class stored in the * value. */ void registerMuzzleVirtualFields(VirtualFieldMappingsBuilder builder); + + /** + * Returns a list of instrumentation helper classes, automatically detected by muzzle during + * compilation. Those helpers will be injected into the application classloader. + */ + List getMuzzleHelperClassNames(); + + /** See {@link #getMuzzleHelperClassNames()}. */ + static List getMuzzleHelperClassNames(InstrumentationModule module) { + if (module instanceof InstrumentationModuleMuzzle) { + return ((InstrumentationModuleMuzzle) module).getMuzzleHelperClassNames(); + } else { + return Collections.emptyList(); + } + } } diff --git a/muzzle/src/main/java/io/opentelemetry/javaagent/tooling/muzzle/Mismatch.java b/muzzle/src/main/java/io/opentelemetry/javaagent/tooling/muzzle/Mismatch.java index 5ddedb3b96..12fba47717 100644 --- a/muzzle/src/main/java/io/opentelemetry/javaagent/tooling/muzzle/Mismatch.java +++ b/muzzle/src/main/java/io/opentelemetry/javaagent/tooling/muzzle/Mismatch.java @@ -185,8 +185,8 @@ public abstract class Mismatch { } /** - * Represents failure to inject {@link InstrumentationModule#getMuzzleHelperClassNames()} into - * some classloader. + * Represents failure to inject {@link InstrumentationModuleMuzzle#getMuzzleHelperClassNames()} + * into some classloader. */ public static class HelperClassesInjectionError extends Mismatch { diff --git a/muzzle/src/main/java/io/opentelemetry/javaagent/tooling/muzzle/MuzzleReferencesAccessor.java b/muzzle/src/main/java/io/opentelemetry/javaagent/tooling/muzzle/MuzzleReferencesAccessor.java deleted file mode 100644 index e5ba874d24..0000000000 --- a/muzzle/src/main/java/io/opentelemetry/javaagent/tooling/muzzle/MuzzleReferencesAccessor.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.javaagent.tooling.muzzle; - -import static java.util.Collections.emptyMap; - -import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; -import io.opentelemetry.javaagent.tooling.muzzle.references.ClassRef; -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodType; -import java.util.Map; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * This class is used to access {@code getMuzzleReferences} method from the {@link - * InstrumentationModule} class. That method is not visible in the source code and instead is - * generated automatically during compilation by the {@code - * io.opentelemetry.instrumentation.javaagent-codegen} Gradle plugin. - */ -class MuzzleReferencesAccessor { - - private static final Logger logger = LoggerFactory.getLogger(MuzzleReferencesAccessor.class); - - private static final ClassValue getMuzzleReferences = - new ClassValue() { - @Override - protected MethodHandle computeValue(Class type) { - MethodHandles.Lookup lookup = MethodHandles.publicLookup(); - MethodHandle handle; - try { - // This method is generated automatically during compilation by - // the io.opentelemetry.instrumentation.javaagent-codegen Gradle plugin. - handle = - lookup.findVirtual(type, "getMuzzleReferences", MethodType.methodType(Map.class)); - } catch (NoSuchMethodException | IllegalAccessException e) { - handle = null; - } - return handle; - } - }; - - /** - * Returns references to helper and library classes used in the given module's type - * instrumentation advices, grouped by {@link ClassRef#getClassName()}. - */ - @SuppressWarnings("unchecked") - static Map getFor(InstrumentationModule instrumentationModule) { - if (instrumentationModule instanceof InstrumentationModuleMuzzle) { - return ((InstrumentationModuleMuzzle) instrumentationModule).getMuzzleReferences(); - } - - // Older classes created and compiled outside of this repo may not yet have the interface above. - MethodHandle methodHandle = getMuzzleReferences.get(instrumentationModule.getClass()); - if (methodHandle != null) { - logger.warn( - "{} is compiled with old version of Muzzle and must be recompiled against newer version of OpenTelemetry Java Instrumentation APIs", - instrumentationModule); - } - return emptyMap(); - } -} diff --git a/muzzle/src/main/java/io/opentelemetry/javaagent/tooling/muzzle/ReferenceMatcher.java b/muzzle/src/main/java/io/opentelemetry/javaagent/tooling/muzzle/ReferenceMatcher.java index d4c06be095..1b7f492d2f 100644 --- a/muzzle/src/main/java/io/opentelemetry/javaagent/tooling/muzzle/ReferenceMatcher.java +++ b/muzzle/src/main/java/io/opentelemetry/javaagent/tooling/muzzle/ReferenceMatcher.java @@ -39,8 +39,8 @@ public final class ReferenceMatcher { public static ReferenceMatcher of(InstrumentationModule instrumentationModule) { return new ReferenceMatcher( - instrumentationModule.getMuzzleHelperClassNames(), - MuzzleReferencesAccessor.getFor(instrumentationModule), + InstrumentationModuleMuzzle.getMuzzleHelperClassNames(instrumentationModule), + InstrumentationModuleMuzzle.getMuzzleReferences(instrumentationModule), instrumentationModule::isHelperClass); } diff --git a/muzzle/src/main/java/io/opentelemetry/javaagent/tooling/muzzle/ReferencesPrinter.java b/muzzle/src/main/java/io/opentelemetry/javaagent/tooling/muzzle/ReferencesPrinter.java index a276a523b9..86e766c883 100644 --- a/muzzle/src/main/java/io/opentelemetry/javaagent/tooling/muzzle/ReferencesPrinter.java +++ b/muzzle/src/main/java/io/opentelemetry/javaagent/tooling/muzzle/ReferencesPrinter.java @@ -22,14 +22,15 @@ public final class ReferencesPrinter { /** * For all {@link InstrumentationModule}s found in the current thread's context classloader this * method prints references returned by the {@link - * MuzzleReferencesAccessor#getFor(InstrumentationModule)} method to the standard output. + * InstrumentationModuleMuzzle#getMuzzleReferences()} method to the standard output. */ public static void printMuzzleReferences() { for (InstrumentationModule instrumentationModule : ServiceLoader.load(InstrumentationModule.class)) { try { System.out.println(instrumentationModule.getClass().getName()); - for (ClassRef ref : MuzzleReferencesAccessor.getFor(instrumentationModule).values()) { + for (ClassRef ref : + InstrumentationModuleMuzzle.getMuzzleReferences(instrumentationModule).values()) { System.out.print(prettyPrint(ref)); } } catch (RuntimeException e) {