Muzzle Assertions and gradle plugin
MODIFY
.circleci/config.yml
.circleci/config.yml
@@ -180,8 +180,8 @@ jobs:
- dd-trace-java-version-scan
- run:
- name: Verify Version Scan
- command: ./gradlew verifyVersionScan --parallel --stacktrace --no-daemon --max-workers=6
+ name: Verify Version Scan and Muzzle
+ command: ./gradlew verifyVersionScan muzzle --parallel --stacktrace --no-daemon --max-workers=6
- save_cache:
key: dd-trace-java-version-scan-{{ checksum "dd-trace-java.gradle" }}

ADD
buildSrc/src/main/groovy/MuzzleExtension.groovy
+class MuzzleExtension {
+ String group
+ String module
+}

ADD
buildSrc/src/main/groovy/MuzzlePlugin.groovy
+import org.gradle.api.Plugin
+import org.gradle.api.Project
+
+import java.lang.reflect.Method
+
+class MuzzlePlugin implements Plugin<Project> {
+ @Override
+ void apply(Project project) {
+ def bootstrapProject = project.rootProject.getChildProjects().get('dd-java-agent').getChildProjects().get('agent-bootstrap')
+ def toolingProject = project.rootProject.getChildProjects().get('dd-java-agent').getChildProjects().get('agent-tooling')
+ project.extensions.create("muzzle", MuzzleExtension)
+ def muzzle = project.task('muzzle') {
+ group = 'Muzzle'
+ description = "Run instrumentation muzzle on compile time dependencies"
+ doLast {
+ List<URL> userUrls = new ArrayList<>()
+ project.getLogger().info("Creating user classpath for: " + project.getName())
+ for (File f : project.configurations.compileOnly.getFiles()) {
+ project.getLogger().info( '--' + f)
+ userUrls.add(f.toURI().toURL())
+ }
+ for (File f : bootstrapProject.sourceSets.main.runtimeClasspath.getFiles()) {
+ project.getLogger().info( '--' + f)
+ userUrls.add(f.toURI().toURL())
+ }
+ final ClassLoader userCL = new URLClassLoader(userUrls.toArray(new URL[0]), (ClassLoader) null)
+
+ project.getLogger().info("Creating dd classpath for: " + project.getName())
+ Set<URL> ddUrls = new HashSet<>()
+ for (File f : toolingProject.sourceSets.main.runtimeClasspath.getFiles()) {
+ project.getLogger().info( '--' + f)
+ ddUrls.add(f.toURI().toURL())
+ }
+ for(File f : project.sourceSets.main.runtimeClasspath.getFiles()) {
+ project.getLogger().info( '--' + f)
+ ddUrls.add(f.toURI().toURL())
+ }
+
+ final ClassLoader agentCL = new URLClassLoader(ddUrls.toArray(new URL[0]), (ClassLoader) null)
+ // find all instrumenters, get muzzle, and assert
+ Method assertionMethod = agentCL.loadClass('datadog.trace.agent.tooling.muzzle.MuzzleGradlePlugin')
+ .getMethod('assertAllInstrumentationIsMuzzled', ClassLoader.class)
+ assertionMethod.invoke(null, userCL)
+ }
+ }
+ // project.tasks.muzzle.dependsOn(bootstrapProject.tasks.shadowJar)
+ project.tasks.muzzle.dependsOn(bootstrapProject.tasks.compileJava)
+ project.tasks.muzzle.dependsOn(toolingProject.tasks.compileJava)
+ project.afterEvaluate {
+ project.tasks.muzzle.dependsOn(project.tasks.compileJava)
+ }
+ }
+}

ADD
buildSrc/src/main/resources/META-INF/gradle-plugins/muzzle.properties
+implementation-class=MuzzlePlugin

MODIFY
dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/Instrumenter.java
dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/Instrumenter.java
@@ -93,9 +93,13 @@ public interface Instrumenter {
// Optimization: calling getMuzzleReferenceMatcher() inside this method prevents unnecessary loading of muzzle references during agentBuilder setup.
final ReferenceMatcher muzzle = getInstrumentationMuzzle();
if (null != muzzle) {
- List<Reference.Mismatch> mismatches = muzzle.getMismatchedReferenceSources(classLoader);
+ List<Reference.Mismatch> mismatches =
+ muzzle.getMismatchedReferenceSources(classLoader);
if (mismatches.size() > 0) {
- log.debug("Instrumentation muzzled: {} on {}", instrumentationPrimaryName, classLoader);
+ log.debug(
+ "Instrumentation muzzled: {} on {}",
+ instrumentationPrimaryName,
+ classLoader);
}
for (Reference.Mismatch mismatch : mismatches) {
log.debug("-- {}", mismatch);

MODIFY
dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/muzzle/MuzzleGradlePlugin.java
dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/muzzle/MuzzleGradlePlugin.java
package datadog.trace.agent.tooling.muzzle;
+import datadog.trace.agent.tooling.HelperInjector;
import datadog.trace.agent.tooling.Instrumenter;
+import java.lang.reflect.Method;
+import java.util.List;
+import java.util.ServiceLoader;
import net.bytebuddy.build.Plugin;
import net.bytebuddy.description.type.TypeDefinition;
import net.bytebuddy.description.type.TypeDescription;
@@ -67,4 +71,55 @@ public class MuzzleGradlePlugin implements Plugin {
return builder;
}
}
+
+ public static void assertAllInstrumentationIsMuzzled(ClassLoader cl) throws Exception {
+ for (Instrumenter instrumenter :
+ ServiceLoader.load(Instrumenter.class, MuzzleGradlePlugin.class.getClassLoader())) {
+ if (instrumenter.getClass().getName().endsWith("TraceConfigInstrumentation")) {
+ // TraceConfigInstrumentation doesn't do muzzle checks
+ // check on TracerClassInstrumentation instead
+ instrumenter =
+ (Instrumenter)
+ MuzzleGradlePlugin.class
+ .getClassLoader()
+ .loadClass(instrumenter.getClass().getName() + "$TracerClassInstrumentation")
+ .getDeclaredConstructor()
+ .newInstance();
+ }
+ Method m = null;
+ try {
+ m = instrumenter.getClass().getDeclaredMethod("getInstrumentationMuzzle");
+ m.setAccessible(true);
+ ReferenceMatcher muzzle = (ReferenceMatcher) m.invoke(instrumenter);
+ List<Reference.Mismatch> mismatches = muzzle.getMismatchedReferenceSources(cl);
+ if (mismatches.size() > 0) {
+ System.err.println(
+ "FAILED MUZZLE VALIDATION: " + instrumenter.getClass().getName() + " mismatches:");
+ for (Reference.Mismatch mismatch : mismatches) {
+ System.err.println("-- " + mismatch);
+ }
+ throw new RuntimeException("Instrumentation failed Muzzle validation");
+ }
+ } finally {
+ if (null != m) {
+ m.setAccessible(false);
+ }
+ }
+ try {
+ // Ratpack injects the scope manager as a helper.
+ // This is likely a bug, but we'll grandfather it out of the helper checks for now.
+ if (!instrumenter.getClass().getName().contains("Ratpack")) {
+ // verify helper injector works
+ final String[] helperClassNames = instrumenter.helperClassNames();
+ if (helperClassNames.length > 0) {
+ new HelperInjector(helperClassNames).transform(null, null, cl, null);
+ }
+ }
+ } catch (Exception e) {
+ System.err.println(
+ "FAILED HELPER INJECTION. Are Helpers being injected in the correct order?");
+ throw e;
+ }
+ }
+ }
}

MODIFY
dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/muzzle/MuzzleVisitor.java
dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/muzzle/MuzzleVisitor.java
@@ -156,7 +156,6 @@ public class MuzzleVisitor implements AsmVisitorWrapper {
"Ldatadog/trace/agent/tooling/muzzle/ReferenceMatcher;");
mv.visitJumpInsn(Opcodes.IF_ACMPNE, ret);
-
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitTypeInsn(Opcodes.NEW, "datadog/trace/agent/tooling/muzzle/ReferenceMatcher");

MODIFY
dd-java-agent/instrumentation/apache-httpclient-4.3/src/main/java/datadog/trace/instrumentation/apachehttpclient/ApacheHttpClientInstrumentation.java
dd-java-agent/instrumentation/apache-httpclient-4.3/src/main/java/datadog/trace/instrumentation/apachehttpclient/ApacheHttpClientInstrumentation.java
@@ -45,7 +45,7 @@ public class ApacheHttpClientInstrumentation extends Instrumenter.Default {
public String[] helperClassNames() {
return new String[] {
"datadog.trace.instrumentation.apachehttpclient.DDTracingClientExec",
- "datadog.trace.instrumentation.apachehttpclient.DDTracingClientExec$HttpHeadersInjectAdapter"
+ "datadog.trace.instrumentation.apachehttpclient.DDTracingClientExec$HttpHeadersInjectAdapter",
};
}

MODIFY
dd-java-agent/instrumentation/instrumentation.gradle
dd-java-agent/instrumentation/instrumentation.gradle
@@ -14,6 +14,7 @@ Project instr_project = project
subprojects { subProj ->
if (subProj.getParent() == instr_project) {
apply plugin: "net.bytebuddy.byte-buddy"
+ apply plugin: 'muzzle'
subProj.byteBuddy {
transformation {

MODIFY
dd-java-agent/instrumentation/mongo-async-3.3/mongo-async-3.3.gradle
dd-java-agent/instrumentation/mongo-async-3.3/mongo-async-3.3.gradle
@@ -17,6 +17,7 @@ dependencies {
compile(project(':dd-java-agent:instrumentation:mongo-3.1')) {
transitive = false
}
+ compileOnly group: 'org.mongodb', name: 'mongo-java-driver', version: '3.4.2'
compileOnly group: 'org.mongodb', name: 'mongodb-driver-async', version: '3.4.2'
compile project(':dd-java-agent:agent-tooling')

Muzzle Assertions and gradle plugin