Skip to content
Open
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
1 change: 1 addition & 0 deletions dd-java-agent/agent-tooling/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ dependencies {

testImplementation project(':dd-java-agent:testing')
testImplementation libs.bytebuddy
testImplementation libs.bundles.junit5
testImplementation group: 'com.google.guava', name: 'guava-testlib', version: '20.0'

jmhImplementation group: 'org.springframework.boot', name: 'spring-boot-starter-web', version: '2.3.5.RELEASE'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,7 @@
public final class HelperScanner extends ClassVisitor {
static final int READER_OPTIONS = ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES;

static final ClassFileLocator locator =
ClassFileLocator.ForClassLoader.of(Utils.getAgentClassLoader());
final ClassFileLocator locator;

final MethodScanner methodScanner = new MethodScanner();

Expand All @@ -41,14 +40,28 @@ public final class HelperScanner extends ClassVisitor {
Set<String> uses;

HelperScanner() {
this(ClassFileLocator.ForClassLoader.of(Utils.getAgentClassLoader()));
}

HelperScanner(ClassFileLocator locator) {
super(Opcodes.ASM7, null);
this.locator = locator;
}

/** Expands helper class names to include any non-bootstrap classes they depend on. */
public static String[] withClassDependencies(String... helperClassNames) {
return new HelperScanner().simulateClassLoading(helperClassNames);
}

/**
* Same as above, but reads bytecode via the passed locator (e.g. during build time when the agent

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

MuzzleGenerator needs to call HelperScanner.withClassDependencies at build-time to expand and order the helpers found, but there's no AgentClassLoader during the build (there is during runtime which is previously the only place we called HelperScanner.withClassDependencies) - so we need to pass in the build classpath's locator to use.

* loader is absent).
*/
public static String[] withClassDependencies(
ClassFileLocator locator, String... helperClassNames) {
return new HelperScanner(locator).simulateClassLoading(helperClassNames);
}

/**
* Simulates class-loading by finding all classes required to load the helper classes as well as
* optional classes used in method instructions that may be needed later when invoking the method.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,9 @@ public static ReferenceMatcher loadStaticMuzzleReferences(
}

/**
* @return Class names of helpers to inject into the user's classloader.
* @return Class names of helpers to inject into the user's classloader. Override this to declare
* them manually; otherwise {@code MuzzleGenerator} generates it at build time from the
* helpers inferred from the advice.
* <p><b>NOTE:</b> The order of the returned helper classes matters. If a muzzle check fails
* with a NoClassDefFoundError, as logged in build/reports/muzzle-*.txt, it is likely that one
* helper class depends on another that appears later in the list. In this case, the returned
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package datadog.trace.agent.tooling.muzzle;

import datadog.trace.bootstrap.Constants;
import java.util.function.Predicate;

/**
* Classifies a referenced class as an injectable tracer helper, a bootstrap class, or a library
* class — similar to OpenTelemetry's {@code HelperClassPredicate#isHelperClass}. The primary signal
* is {@code ownOutput}: a class the instrumentation subproject compiled itself.
*
* <p>A subproject only injects helpers it owns; a helper owned by another subproject must be
* declared explicitly via {@code helperClassNames()}. {@link #HELPER_PREFIXES} lists the shared
* infrastructure subprojects that are not owned by a specific subproject and so are always treated
* as helpers.
*/
public final class HelperClassPredicate {

static final String[] HELPER_PREFIXES = {
"datadog.opentelemetry.shim.",
"datadog.trace.agent.tooling.iast.",
"datadog.trace.agent.tooling.nativeimage.",
};

private final Predicate<String> ownOutput;

/**
* @param ownOutput tests whether a class name was compiled by the instrumentation subproject
* itself; injected so this classifier stays independent of the build directory layout.
*/
public HelperClassPredicate(final Predicate<String> ownOutput) {
this.ownOutput = ownOutput;
}

public boolean isHelperClass(final String className) {
return !isBootstrap(className) && (ownOutput.test(className) || matchesHelperPrefix(className));
}

private static boolean matchesHelperPrefix(final String className) {
for (final String prefix : HELPER_PREFIXES) {
if (className.startsWith(prefix)) {
return true;
}
}
return false;
}

/** Whether the class is on the bootstrap class-path and so never injected. */
public static boolean isBootstrap(final String className) {
if (className.startsWith("java.")
|| className.startsWith("javax.")
|| className.startsWith("jdk.")
|| className.startsWith("com.sun.")
|| className.startsWith("sun.")
|| className.startsWith("org.slf4j.")
|| className.startsWith("datadog.slf4j.")) {
return true;
}
for (final String prefix : Constants.BOOTSTRAP_PACKAGE_PREFIXES) {
if (className.startsWith(prefix)) {
return true;
}
}
return false;
}
}
Loading