More classloading issues with Glassfish

Glassfish’s WebappClassLoader caches when a resource or class load fails, so we can’t load as a resource first to see if it is available.

Also add additional logging.
This commit is contained in:
Tyler Benson 2019-04-15 17:46:28 -07:00
parent 2c5ae9f3b1
commit ac734ac6ee
4 changed files with 34 additions and 14 deletions

View File

@ -7,23 +7,23 @@ import net.bytebuddy.dynamic.ClassFileLocator;
import net.bytebuddy.utility.JavaModule; import net.bytebuddy.utility.JavaModule;
/** /**
* Locate resources with the loading classloader. If the loading classloader cannot find the desired * Locate resources with the loading classloader. Because of a quirk with the way classes appended
* resource, check up the classloader hierarchy until a resource is found or the bootstrap loader is * to the bootstrap classpath work, we first check our bootstrap proxy. If the loading classloader
* reached. * cannot find the desired resource, check up the classloader hierarchy until a resource is found.
*/ */
public class DDLocationStrategy implements AgentBuilder.LocationStrategy { public class DDLocationStrategy implements AgentBuilder.LocationStrategy {
public ClassFileLocator classFileLocator(ClassLoader classLoader) { public ClassFileLocator classFileLocator(final ClassLoader classLoader) {
return classFileLocator(classLoader, null); return classFileLocator(classLoader, null);
} }
@Override @Override
public ClassFileLocator classFileLocator(ClassLoader classLoader, final JavaModule javaModule) { public ClassFileLocator classFileLocator(ClassLoader classLoader, final JavaModule javaModule) {
final List<ClassFileLocator> locators = new ArrayList<>(); final List<ClassFileLocator> locators = new ArrayList<>();
locators.add(ClassFileLocator.ForClassLoader.of(Utils.getBootstrapProxy()));
while (classLoader != null) { while (classLoader != null) {
locators.add(ClassFileLocator.ForClassLoader.WeaklyReferenced.of(classLoader)); locators.add(ClassFileLocator.ForClassLoader.WeaklyReferenced.of(classLoader));
classLoader = classLoader.getParent(); classLoader = classLoader.getParent();
} }
locators.add(ClassFileLocator.ForClassLoader.of(Utils.getBootstrapProxy()));
return new ClassFileLocator.Compound(locators.toArray(new ClassFileLocator[0])); return new ClassFileLocator.Compound(locators.toArray(new ClassFileLocator[0]));
} }
} }

View File

@ -31,6 +31,9 @@ public class ExceptionHandlers {
@Override @Override
public Size apply(final MethodVisitor mv, final Implementation.Context context) { public Size apply(final MethodVisitor mv, final Implementation.Context context) {
final String name = context.getInstrumentedType().getName();
final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
// writes the following bytecode: // writes the following bytecode:
// try { // try {
// org.slf4j.LoggerFactory.getLogger((Class)ExceptionLogger.class) // org.slf4j.LoggerFactory.getLogger((Class)ExceptionLogger.class)
@ -58,7 +61,11 @@ public class ExceptionHandlers {
"(Ljava/lang/Class;)L" + LOGGER_NAME + ";", "(Ljava/lang/Class;)L" + LOGGER_NAME + ";",
false); false);
mv.visitInsn(Opcodes.SWAP); // stack: (top) throwable,logger mv.visitInsn(Opcodes.SWAP); // stack: (top) throwable,logger
mv.visitLdcInsn("Failed to handle exception in instrumentation"); mv.visitLdcInsn(
"Failed to handle exception in instrumentation for "
+ name
+ " on "
+ classLoader);
mv.visitInsn(Opcodes.SWAP); // stack: (top) throwable,string,logger mv.visitInsn(Opcodes.SWAP); // stack: (top) throwable,string,logger
mv.visitMethodInsn( mv.visitMethodInsn(
Opcodes.INVOKEINTERFACE, Opcodes.INVOKEINTERFACE,

View File

@ -98,7 +98,7 @@ public interface Instrumenter {
agentBuilder = agentBuilder =
agentBuilder.transform( agentBuilder.transform(
new AgentBuilder.Transformer.ForAdvice() new AgentBuilder.Transformer.ForAdvice()
.include(Utils.getAgentClassLoader()) .include(Utils.getBootstrapProxy(), Utils.getAgentClassLoader())
.withExceptionHandler(ExceptionHandlers.defaultExceptionHandler()) .withExceptionHandler(ExceptionHandlers.defaultExceptionHandler())
.advice(entry.getKey(), entry.getValue())); .advice(entry.getKey(), entry.getValue()));
} }

View File

@ -1,17 +1,15 @@
package datadog.trace.agent.integration.classloading package datadog.trace.agent.integration.classloading
import static datadog.trace.agent.test.IntegrationTestUtils.createJarWithClasses
import datadog.test.ClassToInstrument import datadog.test.ClassToInstrument
import datadog.test.ClassToInstrumentChild import datadog.test.ClassToInstrumentChild
import datadog.trace.agent.test.IntegrationTestUtils
import datadog.trace.api.Trace import datadog.trace.api.Trace
import datadog.trace.util.gc.GCUtils import datadog.trace.util.gc.GCUtils
import java.lang.ref.WeakReference
import spock.lang.Specification import spock.lang.Specification
import spock.lang.Timeout import spock.lang.Timeout
import java.lang.ref.WeakReference
import static datadog.trace.agent.test.IntegrationTestUtils.createJarWithClasses
@Timeout(10) @Timeout(10)
class ClassLoadingTest extends Specification { class ClassLoadingTest extends Specification {
@ -94,8 +92,23 @@ class ClassLoadingTest extends Specification {
loader1.count == loader2.count loader1.count == loader2.count
} }
def "can find bootstrap resources"() { def "can find classes but not resources loaded onto the bootstrap classpath"() {
expect: expect:
IntegrationTestUtils.getAgentClassLoader().getResources('datadog/trace/api/Trace.class') != null Class.forName(name) != null
// Resources from bootstrap injected jars can't be loaded.
// https://github.com/raphw/byte-buddy/pull/496
if (onTestClasspath) {
assert ClassLoader.getSystemClassLoader().getResource(resource) != null
} else {
assert ClassLoader.getSystemClassLoader().getResource(resource) == null
}
where:
name | onTestClasspath
"datadog.trace.api.Trace" | true
"datadog.trace.bootstrap.instrumentation.java.concurrent.State" | false
resource = name.replace(".", "/") + ".class"
} }
} }