Skip to content
Closed
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 @@ -36,11 +36,6 @@ public void registerHelperResources(HelperResourceBuilder helperResourceBuilder)
"test-resources/test-resource.txt", "test-resources/test-resource-2.txt");
}

@Override
public boolean isIndyModule() {
return false;
}

public static class TestTypeInstrumentation implements TypeInstrumentation {
@Override
public ElementMatcher<ClassLoader> classLoaderOptimization() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.javaagent.instrumentation.internal.classloader;

import static java.util.logging.Level.WARNING;

import io.opentelemetry.javaagent.bootstrap.BootstrapPackagePrefixesHolder;
import io.opentelemetry.javaagent.bootstrap.CallDepth;
import io.opentelemetry.javaagent.tooling.Constants;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.List;
import java.util.logging.Logger;

public class BootDelegationHelper {

private BootDelegationHelper() {}

public static class Holder {

public static final List<String> bootstrapPackagesPrefixes = findBootstrapPackagePrefixes();

/**
* We have to make sure that {@link BootstrapPackagePrefixesHolder} is loaded from bootstrap
* class loader. After that we can use in {@link BootDelegationHelper}.
*/
private static List<String> findBootstrapPackagePrefixes() {
try {
Class<?> holderClass =
Class.forName(
"io.opentelemetry.javaagent.bootstrap.BootstrapPackagePrefixesHolder", true, null);
MethodHandle methodHandle =
MethodHandles.publicLookup()
.findStatic(
holderClass, "getBoostrapPackagePrefixes", MethodType.methodType(List.class));
//noinspection unchecked
return (List<String>) methodHandle.invokeExact();
} catch (Throwable e) {
Logger.getLogger(Holder.class.getName())
.log(WARNING, "Unable to load bootstrap package prefixes from the bootstrap CL", e);
return Constants.BOOTSTRAP_PACKAGE_PREFIXES;
}
}

private Holder() {}
}

public static Class<?> onEnter(String name) {
// need to use call depth here to prevent re-entry from call to Class.forName() below
// because on some JVMs (e.g. IBM's, though IBM bootstrap loader is explicitly excluded above)
// Class.forName() ends up calling loadClass() on the bootstrap loader which would then come
// back to this instrumentation over and over, causing a StackOverflowError
CallDepth callDepth = CallDepth.forClass(ClassLoader.class);
if (callDepth.getAndIncrement() > 0) {
callDepth.decrementAndGet();
return null;
}

try {
for (String prefix : Holder.bootstrapPackagesPrefixes) {
if (name.startsWith(prefix)) {
try {
System.out.println("trying bootstrap for class " + name);
return Class.forName(name, false, null);
} catch (ClassNotFoundException ignored) {
// Ignore
}
}
}
} finally {
// need to reset it right away, not waiting until method exit
// otherwise it will prevent this instrumentation from being applied when loadClass()
// ends up calling a ClassFileTransformer which ends up calling loadClass() further down the
// stack on one of our bootstrap packages (since the call depth check would then suppress
// the nested loadClass instrumentation)
callDepth.decrementAndGet();
}
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,28 +6,12 @@
package io.opentelemetry.javaagent.instrumentation.internal.classloader;

import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.extendsClass;
import static java.util.logging.Level.WARNING;
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.isProtected;
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
import static net.bytebuddy.matcher.ElementMatchers.isStatic;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.namedOneOf;
import static net.bytebuddy.matcher.ElementMatchers.not;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;

import io.opentelemetry.javaagent.bootstrap.BootstrapPackagePrefixesHolder;
import io.opentelemetry.javaagent.bootstrap.CallDepth;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import io.opentelemetry.javaagent.tooling.Constants;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.List;
import java.util.logging.Logger;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;

Expand Down Expand Up @@ -56,93 +40,7 @@ public ElementMatcher<TypeDescription> typeMatcher() {

@Override
public void transform(TypeTransformer transformer) {
transformer.applyAdviceToMethod(
isMethod()
.and(named("loadClass"))
.and(
takesArguments(1)
.and(takesArgument(0, String.class))
.or(
takesArguments(2)
.and(takesArgument(0, String.class))
.and(takesArgument(1, boolean.class))))
.and(isPublic().or(isProtected()))
.and(not(isStatic())),
BootDelegationInstrumentation.class.getName() + "$LoadClassAdvice");
}

public static class Holder {

public static final List<String> bootstrapPackagesPrefixes = findBootstrapPackagePrefixes();

/**
* We have to make sure that {@link BootstrapPackagePrefixesHolder} is loaded from bootstrap
* class loader. After that we can use in {@link LoadClassAdvice}.
*/
private static List<String> findBootstrapPackagePrefixes() {
try {
Class<?> holderClass =
Class.forName(
"io.opentelemetry.javaagent.bootstrap.BootstrapPackagePrefixesHolder", true, null);
MethodHandle methodHandle =
MethodHandles.publicLookup()
.findStatic(
holderClass, "getBoostrapPackagePrefixes", MethodType.methodType(List.class));
//noinspection unchecked
return (List<String>) methodHandle.invokeExact();
} catch (Throwable e) {
Logger.getLogger(Holder.class.getName())
.log(WARNING, "Unable to load bootstrap package prefixes from the bootstrap CL", e);
return Constants.BOOTSTRAP_PACKAGE_PREFIXES;
}
}

private Holder() {}
}

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

@Advice.OnMethodEnter(skipOn = Advice.OnNonDefaultValue.class)
public static Class<?> onEnter(@Advice.Argument(0) String name) {
// need to use call depth here to prevent re-entry from call to Class.forName() below
// because on some JVMs (e.g. IBM's, though IBM bootstrap loader is explicitly excluded above)
// Class.forName() ends up calling loadClass() on the bootstrap loader which would then come
// back to this instrumentation over and over, causing a StackOverflowError
CallDepth callDepth = CallDepth.forClass(ClassLoader.class);
if (callDepth.getAndIncrement() > 0) {
callDepth.decrementAndGet();
return null;
}

try {
for (String prefix : Holder.bootstrapPackagesPrefixes) {
if (name.startsWith(prefix)) {
try {
return Class.forName(name, false, null);
} catch (ClassNotFoundException ignored) {
// Ignore
}
}
}
} finally {
// need to reset it right away, not waiting until onExit()
// otherwise it will prevent this instrumentation from being applied when loadClass()
// ends up calling a ClassFileTransformer which ends up calling loadClass() further down the
// stack on one of our bootstrap packages (since the call depth check would then suppress
// the nested loadClass instrumentation)
callDepth.decrementAndGet();
}
return null;
}

@Advice.OnMethodExit(onThrowable = Throwable.class)
public static void onExit(
@Advice.Return(readOnly = false) Class<?> result,
@Advice.Enter Class<?> resultFromBootstrapLoader) {
if (resultFromBootstrapLoader != null) {
result = resultFromBootstrapLoader;
}
}
// we must use inlined instrumentation with ASM to prevent stack overflow errors
transformer.applyTransformer(ClassLoaderAsmUtil.getBootDelegationTransformer());
}
}
Loading
Loading