Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Remove generated InstrumentationModule#getMuzzleReferences() method from the source code #4087

Merged
merged 4 commits into from
Sep 13, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@

import io.opentelemetry.instrumentation.api.config.Config;
import io.opentelemetry.javaagent.extension.Ordered;
import io.opentelemetry.javaagent.extension.muzzle.ClassRef;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashSet;
Expand Down Expand Up @@ -148,20 +147,6 @@ public ElementMatcher.Junction<ClassLoader> classLoaderMatcher() {
/** Returns a list of all individual type instrumentation in this module. */
public abstract List<TypeInstrumentation> typeInstrumentations();

/**
* Returns references to helper and library classes used in this module's type instrumentation
* advices, grouped by {@link ClassRef#getClassName()}.
*
* <p>The actual implementation of this method is generated automatically during compilation by
* the {@code io.opentelemetry.instrumentation.javaagent-codegen} Gradle plugin.
*
* <p><b>This method is generated automatically</b>: if you override it, the muzzle compile plugin
* will not generate a new implementation, it will leave the existing one.
*/
public Map<String, ClassRef> getMuzzleReferences() {
return Collections.emptyMap();
}

/**
* Returns a list of instrumentation helper classes, automatically detected by muzzle during
* compilation. Those helpers will be injected into the application classloader.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,14 @@
import static io.opentelemetry.javaagent.extension.muzzle.ReferenceMergeUtil.mergeMethods;
import static io.opentelemetry.javaagent.extension.muzzle.ReferenceMergeUtil.mergeSet;

import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
import java.util.Set;
import org.checkerframework.checker.nullness.qual.Nullable;

/**
* Represents a reference to a class used in the instrumentation advice or helper class code (or the
* helper class itself).
*
* <p>This class is used in the auto-generated {@link InstrumentationModule#getMuzzleReferences()}
* <p>This class is used in the auto-generated {@code InstrumentationModule#getMuzzleReferences()}
* method, it is not meant to be used directly by agent extension developers.
*/
public final class ClassRef {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import static java.util.Arrays.asList;
import static java.util.Collections.emptySet;

import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashSet;
Expand All @@ -20,7 +19,7 @@
/**
* The builder of {@link ClassRef}.
*
* <p>This class is used in the auto-generated {@link InstrumentationModule#getMuzzleReferences()}
* <p>This class is used in the auto-generated {@code InstrumentationModule#getMuzzleReferences()}
* method, it is not meant to be used directly by agent extension developers.
*/
public final class ClassRefBuilder {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import static io.opentelemetry.javaagent.extension.muzzle.ReferenceMergeUtil.mergeFlags;
import static io.opentelemetry.javaagent.extension.muzzle.ReferenceMergeUtil.mergeSet;

import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
import java.util.Set;
import java.util.stream.Collectors;
import net.bytebuddy.jar.asm.Type;
Expand All @@ -17,7 +16,7 @@
* Represents a reference to a field used in the instrumentation advice or helper class code. Part
* of a {@link ClassRef}.
*
* <p>This class is used in the auto-generated {@link InstrumentationModule#getMuzzleReferences()}
* <p>This class is used in the auto-generated {@code InstrumentationModule#getMuzzleReferences()}
* method, it is not meant to be used directly by agent extension developers.
*/
public final class FieldRef {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,12 @@

package io.opentelemetry.javaagent.extension.muzzle;

import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
import net.bytebuddy.jar.asm.Opcodes;

/**
* Expected flag (or lack of flag) on a class, method or field reference.
*
* <p>This class is used in the auto-generated {@link InstrumentationModule#getMuzzleReferences()}
* <p>This class is used in the auto-generated {@code InstrumentationModule#getMuzzleReferences()}
* method, it is not meant to be used directly by agent extension developers.
*/
public interface Flag {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import static io.opentelemetry.javaagent.extension.muzzle.ReferenceMergeUtil.mergeFlags;
import static io.opentelemetry.javaagent.extension.muzzle.ReferenceMergeUtil.mergeSet;

import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
Expand All @@ -19,7 +18,7 @@
* Represents a reference to a method used in the instrumentation advice or helper class code. Part
* of a {@link ClassRef}.
*
* <p>This class is used in the auto-generated {@link InstrumentationModule#getMuzzleReferences()}
* <p>This class is used in the auto-generated {@code InstrumentationModule#getMuzzleReferences()}
* method, it is not meant to be used directly by agent extension developers.
*/
public final class MethodRef {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,12 @@

package io.opentelemetry.javaagent.extension.muzzle;

import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
import java.util.Objects;

/**
* Represents the source (file name, line number) of a reference.
*
* <p>This class is used in the auto-generated {@link InstrumentationModule#getMuzzleReferences()}
* <p>This class is used in the auto-generated {@code InstrumentationModule#getMuzzleReferences()}
* method, it is not meant to be used directly by agent extension developers.
*/
public final class Source {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ AgentBuilder install(

ElementMatcher.Junction<ClassLoader> moduleClassLoaderMatcher =
instrumentationModule.classLoaderMatcher();
MuzzleMatcher muzzleMatcher = new MuzzleMatcher(instrumentationModule, helperClassNames);
MuzzleMatcher muzzleMatcher = new MuzzleMatcher(instrumentationModule);
AgentBuilder.Transformer helperInjector =
new HelperInjector(
instrumentationModule.instrumentationName(),
Expand Down Expand Up @@ -148,14 +148,11 @@ static FieldBackedProvider get(
*/
private static class MuzzleMatcher implements AgentBuilder.RawMatcher {
private final InstrumentationModule instrumentationModule;
private final List<String> helperClassNames;
private final AtomicBoolean initialized = new AtomicBoolean(false);
private volatile ReferenceMatcher referenceMatcher;

private MuzzleMatcher(
InstrumentationModule instrumentationModule, List<String> helperClassNames) {
private MuzzleMatcher(InstrumentationModule instrumentationModule) {
this.instrumentationModule = instrumentationModule;
this.helperClassNames = helperClassNames;
}

@Override
Expand Down Expand Up @@ -202,11 +199,7 @@ public boolean matches(
// during the agent setup
private ReferenceMatcher getReferenceMatcher() {
if (initialized.compareAndSet(false, true)) {
referenceMatcher =
new ReferenceMatcher(
helperClassNames,
instrumentationModule.getMuzzleReferences(),
instrumentationModule::isHelperClass);
referenceMatcher = ReferenceMatcher.of(instrumentationModule);
}
return referenceMatcher;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,7 @@ private static List<Mismatch> matches(

private static List<Mismatch> checkReferenceMatcher(
InstrumentationModule instrumentationModule, ClassLoader classLoader) {
ReferenceMatcher muzzle =
new ReferenceMatcher(
instrumentationModule.getMuzzleHelperClassNames(),
instrumentationModule.getMuzzleReferences(),
instrumentationModule::isHelperClass);
ReferenceMatcher muzzle = ReferenceMatcher.of(instrumentationModule);
return muzzle.getMismatchedReferenceSources(classLoader);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* 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.extension.muzzle.ClassRef;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.Map;

/**
* 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 {
Copy link
Contributor

Choose a reason for hiding this comment

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

Perhaps javaagent-codegen should add an interface that has getMuzzleReferences to instrumentation module so you wouldn't need to use reflection.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I assume that MethodHandle has no performance overhead on invocation. But I will think about this idea, it may be more elegant indeed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I will try this idea as a follow-up optimisation. I like it, but prefer to finish current iteration of removing ClassRef and friends from public API.

Copy link
Member

Choose a reason for hiding this comment

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

This will also be easier to extend with other muzzle-generated methods.

Copy link
Contributor

Choose a reason for hiding this comment

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

If it is possible to build extension jars that contain instrumentation modules then all of the muzzle and codegen stuff has become part of our api. Have you thought about how to version it and how to handle cases where agent is incompatible with codegen version used?

Copy link
Contributor Author

@iNikem iNikem Sep 13, 2021

Choose a reason for hiding this comment

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

Ohhh, good question... The only vague thought I have atm is "javaagent must support all previous stable versions of codegen within any given major version." Which assumes "in sync" versioning of the agent and gradle plugins.

Copy link
Member

Choose a reason for hiding this comment

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

If it is possible to build extension jars that contain instrumentation modules then all of the muzzle and codegen stuff has become part of our api

oh no, this is a great point, so maybe we shouldn't get rid of these classes from our public API? 😭

Copy link
Contributor Author

Choose a reason for hiding this comment

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

These classes should be part of the contract between muzzle generation and muzzle verification. But no end-user, including extension authors, should see them.

private static final ClassValue<MethodHandle> getMuzzleReferences =
new ClassValue<MethodHandle>() {
Copy link
Member

Choose a reason for hiding this comment

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

Now that I think of it we don't really need to use ClassValues and MethodHandles - the getMuzzleReferences() will be called exactly once per InstrumentationModule implementation, so there's no need to cache the result here (it's already cached in the MuzzleMatcher).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Are you sure? Will be happy to remove this cache!

Copy link
Member

Choose a reason for hiding this comment

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

Yep -- the only place (MuzzleMatcher) that constructs ReferenceMatcher in the agent uses an AtomicBoolean initialized field to make sure it only does it once (building a giant map of references can get expensive).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

What about io.opentelemetry.javaagent.tooling.muzzle.ClassLoaderMatcher#matchesAll?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Also, I will try Lauri idea from #4087 (comment) anyway later on. That will probably remove this whole class.

Copy link
Member

Choose a reason for hiding this comment

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

That one is only used in the gradle muzzle-check plugin, it doesn't affect the agent at all.

@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<String, ClassRef> getFor(InstrumentationModule instrumentationModule) {
Map<String, ClassRef> muzzleReferences = emptyMap();
MethodHandle methodHandle = getMuzzleReferences.get(instrumentationModule.getClass());
if (methodHandle != null) {
try {
muzzleReferences = (Map<String, ClassRef>) methodHandle.invoke(instrumentationModule);
} catch (Throwable ignored) {
// silence error prone
}
}
return muzzleReferences;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import static java.util.Collections.singletonList;

import io.opentelemetry.instrumentation.api.caching.Cache;
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
import io.opentelemetry.javaagent.extension.muzzle.ClassRef;
import io.opentelemetry.javaagent.extension.muzzle.FieldRef;
import io.opentelemetry.javaagent.extension.muzzle.Flag;
Expand Down Expand Up @@ -36,7 +37,14 @@ public final class ReferenceMatcher {
private final Set<String> helperClassNames;
private final InstrumentationClassPredicate instrumentationClassPredicate;

public ReferenceMatcher(
public static ReferenceMatcher of(InstrumentationModule instrumentationModule) {
Copy link
Member

Choose a reason for hiding this comment

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

👍

return new ReferenceMatcher(
instrumentationModule.getMuzzleHelperClassNames(),
MuzzleReferencesAccessor.getFor(instrumentationModule),
instrumentationModule::isHelperClass);
}

ReferenceMatcher(
List<String> helperClassNames,
Map<String, ClassRef> references,
Predicate<String> libraryInstrumentationPredicate) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,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 InstrumentationModule#getMuzzleReferences()}
* method to the standard output.
* method prints references returned by the {@link
* MuzzleReferencesAccessor#getFor(InstrumentationModule)} 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 : instrumentationModule.getMuzzleReferences().values()) {
for (ClassRef ref : MuzzleReferencesAccessor.getFor(instrumentationModule).values()) {
System.out.print(prettyPrint(ref));
}
} catch (RuntimeException e) {
Expand Down
Loading