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

make internal-reflection indy compatible #12136

Merged
merged 10 commits into from
Sep 4, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -53,21 +53,27 @@ public void transform(TypeTransformer transformer) {

@SuppressWarnings("unused")
public static class FilterFieldsAdvice {

// using AsScalar is needed to return the array itself instead of "advice Object[] return"
@Advice.AssignReturned.ToReturned
@Advice.AssignReturned.AsScalar
@Advice.OnMethodExit(suppress = Throwable.class)
public static void filter(
@Advice.Argument(0) Class<?> containingClass,
@Advice.Return(readOnly = false) Field[] fields) {
fields = ReflectionHelper.filterFields(containingClass, fields);
public static Field[] filter(
@Advice.Argument(0) Class<?> containingClass, @Advice.Return Field[] fields) {
return ReflectionHelper.filterFields(containingClass, fields);
}
}

@SuppressWarnings("unused")
public static class FilterMethodsAdvice {

// using AsScalar is needed to return the array itself instead of "advice Object[] return"
@Advice.AssignReturned.ToReturned
@Advice.AssignReturned.AsScalar
@Advice.OnMethodExit(suppress = Throwable.class)
public static void filter(
@Advice.Argument(0) Class<?> containingClass,
@Advice.Return(readOnly = false) Method[] methods) {
methods = ReflectionHelper.filterMethods(containingClass, methods);
public static Method[] filter(
@Advice.Argument(0) Class<?> containingClass, @Advice.Return Method[] methods) {
return ReflectionHelper.filterMethods(containingClass, methods);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,15 @@
import com.google.auto.service.AutoService;
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.internal.ExperimentalInstrumentationModule;
import io.opentelemetry.javaagent.extension.instrumentation.internal.injection.ClassInjector;
import io.opentelemetry.javaagent.extension.instrumentation.internal.injection.InjectionMode;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import java.util.List;

@AutoService(InstrumentationModule.class)
public class ReflectionInstrumentationModule extends InstrumentationModule {
public class ReflectionInstrumentationModule extends InstrumentationModule
implements ExperimentalInstrumentationModule {
public ReflectionInstrumentationModule() {
super("internal-reflection");
}
Expand All @@ -26,12 +30,18 @@ public boolean defaultEnabled(ConfigProperties config) {
}

@Override
public boolean isIndyModule() {
return false;
public List<TypeInstrumentation> typeInstrumentations() {
return asList(new ClassInstrumentation(), new ReflectionInstrumentation());
}

@Override
public List<TypeInstrumentation> typeInstrumentations() {
return asList(new ClassInstrumentation(), new ReflectionInstrumentation());
public void injectClasses(ClassInjector injector) {
// we do not use ByteBuddy Advice dispatching in this instrumentation
// Instead, we manually call ReflectionHelper via ASM
// Easiest solution to work with indy is to inject an indy-proxy to be invoked
injector
.proxyBuilder(
"io.opentelemetry.javaagent.instrumentation.internal.reflection.ReflectionHelper")
SylvainJuge marked this conversation as resolved.
Show resolved Hide resolved
.inject(InjectionMode.CLASS_ONLY);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -109,10 +109,17 @@ public MethodHandles.Lookup getLookup() {
if (cachedLookup == null) {
// Load the injected copy of LookupExposer and invoke it
try {
MethodType getLookupType = MethodType.methodType(MethodHandles.Lookup.class);
// we don't mind the race condition causing the initialization to run multiple times here
Class<?> lookupExposer = loadClass(LookupExposer.class.getName());
cachedLookup = (MethodHandles.Lookup) lookupExposer.getMethod("getLookup").invoke(null);
} catch (Exception e) {
// Note: we must use MethodHandles instead of reflection here to avoid a recursion
// for our internal ReflectionInstrumentationModule which instruments reflection methods
cachedLookup =
(MethodHandles.Lookup)
MethodHandles.publicLookup()
.findStatic(lookupExposer, "getLookup", getLookupType)
.invoke();
} catch (Throwable e) {
throw new IllegalStateException(e);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,8 @@ public DynamicType.Builder<?> transform(
injectedClassLoaders.computeIfAbsent(
maskNullClassLoader(classLoader),
cl -> {
List<HelperClassDefinition> helpers = helperClassesGenerator.apply(cl);
List<HelperClassDefinition> helpers =
helperClassesGenerator.apply(unmaskNullClassLoader(cl));

LinkedHashMap<String, Supplier<byte[]>> classesToInject =
helpers.stream()
Expand Down Expand Up @@ -355,6 +356,10 @@ private static ClassLoader maskNullClassLoader(ClassLoader classLoader) {
return classLoader != null ? classLoader : BOOTSTRAP_CLASSLOADER_PLACEHOLDER;
}

private static ClassLoader unmaskNullClassLoader(ClassLoader classLoader) {
return isBootClassLoader(classLoader) ? null : classLoader;
}

private static boolean isBootClassLoader(ClassLoader classLoader) {
return classLoader == BOOTSTRAP_CLASSLOADER_PLACEHOLDER;
}
Expand Down
Loading