make internal-reflection indy compatible (#12136)
Co-authored-by: Jonas Kunz <jonas.kunz@elastic.co>
This commit is contained in:
parent
182e938411
commit
4b03e98b47
|
@ -53,21 +53,27 @@ public class ReflectionInstrumentation implements TypeInstrumentation {
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
public static class FilterFieldsAdvice {
|
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)
|
@Advice.OnMethodExit(suppress = Throwable.class)
|
||||||
public static void filter(
|
public static Field[] filter(
|
||||||
@Advice.Argument(0) Class<?> containingClass,
|
@Advice.Argument(0) Class<?> containingClass, @Advice.Return Field[] fields) {
|
||||||
@Advice.Return(readOnly = false) Field[] fields) {
|
return ReflectionHelper.filterFields(containingClass, fields);
|
||||||
fields = ReflectionHelper.filterFields(containingClass, fields);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
public static class FilterMethodsAdvice {
|
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)
|
@Advice.OnMethodExit(suppress = Throwable.class)
|
||||||
public static void filter(
|
public static Method[] filter(
|
||||||
@Advice.Argument(0) Class<?> containingClass,
|
@Advice.Argument(0) Class<?> containingClass, @Advice.Return Method[] methods) {
|
||||||
@Advice.Return(readOnly = false) Method[] methods) {
|
return ReflectionHelper.filterMethods(containingClass, methods);
|
||||||
methods = ReflectionHelper.filterMethods(containingClass, methods);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,11 +10,15 @@ import static java.util.Arrays.asList;
|
||||||
import com.google.auto.service.AutoService;
|
import com.google.auto.service.AutoService;
|
||||||
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
|
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
|
||||||
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
|
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 io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@AutoService(InstrumentationModule.class)
|
@AutoService(InstrumentationModule.class)
|
||||||
public class ReflectionInstrumentationModule extends InstrumentationModule {
|
public class ReflectionInstrumentationModule extends InstrumentationModule
|
||||||
|
implements ExperimentalInstrumentationModule {
|
||||||
public ReflectionInstrumentationModule() {
|
public ReflectionInstrumentationModule() {
|
||||||
super("internal-reflection");
|
super("internal-reflection");
|
||||||
}
|
}
|
||||||
|
@ -25,13 +29,19 @@ public class ReflectionInstrumentationModule extends InstrumentationModule {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isIndyModule() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<TypeInstrumentation> typeInstrumentations() {
|
public List<TypeInstrumentation> typeInstrumentations() {
|
||||||
return asList(new ClassInstrumentation(), new ReflectionInstrumentation());
|
return asList(new ClassInstrumentation(), new ReflectionInstrumentation());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
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")
|
||||||
|
.inject(InjectionMode.CLASS_ONLY);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -109,10 +109,17 @@ public class InstrumentationModuleClassLoader extends ClassLoader {
|
||||||
if (cachedLookup == null) {
|
if (cachedLookup == null) {
|
||||||
// Load the injected copy of LookupExposer and invoke it
|
// Load the injected copy of LookupExposer and invoke it
|
||||||
try {
|
try {
|
||||||
|
MethodType getLookupType = MethodType.methodType(MethodHandles.Lookup.class);
|
||||||
// we don't mind the race condition causing the initialization to run multiple times here
|
// we don't mind the race condition causing the initialization to run multiple times here
|
||||||
Class<?> lookupExposer = loadClass(LookupExposer.class.getName());
|
Class<?> lookupExposer = loadClass(LookupExposer.class.getName());
|
||||||
cachedLookup = (MethodHandles.Lookup) lookupExposer.getMethod("getLookup").invoke(null);
|
// Note: we must use MethodHandles instead of reflection here to avoid a recursion
|
||||||
} catch (Exception e) {
|
// 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);
|
throw new IllegalStateException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -172,7 +172,8 @@ public class HelperInjector implements Transformer {
|
||||||
injectedClassLoaders.computeIfAbsent(
|
injectedClassLoaders.computeIfAbsent(
|
||||||
maskNullClassLoader(classLoader),
|
maskNullClassLoader(classLoader),
|
||||||
cl -> {
|
cl -> {
|
||||||
List<HelperClassDefinition> helpers = helperClassesGenerator.apply(cl);
|
List<HelperClassDefinition> helpers =
|
||||||
|
helperClassesGenerator.apply(unmaskNullClassLoader(cl));
|
||||||
|
|
||||||
LinkedHashMap<String, Supplier<byte[]>> classesToInject =
|
LinkedHashMap<String, Supplier<byte[]>> classesToInject =
|
||||||
helpers.stream()
|
helpers.stream()
|
||||||
|
@ -355,6 +356,10 @@ public class HelperInjector implements Transformer {
|
||||||
return classLoader != null ? classLoader : BOOTSTRAP_CLASSLOADER_PLACEHOLDER;
|
return classLoader != null ? classLoader : BOOTSTRAP_CLASSLOADER_PLACEHOLDER;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static ClassLoader unmaskNullClassLoader(ClassLoader classLoader) {
|
||||||
|
return isBootClassLoader(classLoader) ? null : classLoader;
|
||||||
|
}
|
||||||
|
|
||||||
private static boolean isBootClassLoader(ClassLoader classLoader) {
|
private static boolean isBootClassLoader(ClassLoader classLoader) {
|
||||||
return classLoader == BOOTSTRAP_CLASSLOADER_PLACEHOLDER;
|
return classLoader == BOOTSTRAP_CLASSLOADER_PLACEHOLDER;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue