Muzzle Assertions and gradle plugin

This commit is contained in:
Andrew Kent 2018-07-09 14:06:44 -04:00
parent 541a6998a4
commit 628f4929dc
11 changed files with 139 additions and 21 deletions

View File

@ -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" }}

View File

@ -0,0 +1,4 @@
class MuzzleExtension {
String group
String module
}

View File

@ -0,0 +1,53 @@
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)
}
}
}

View File

@ -0,0 +1 @@
implementation-class=MuzzlePlugin

View File

@ -90,10 +90,10 @@ public class AgentInstaller {
final boolean loaded,
final Throwable throwable) {
log.debug(
"Failed to handle {} for transformation on classloader {}: {}",
typeName,
classLoader,
throwable.getMessage());
"Failed to handle {} for transformation on classloader {}: {}",
typeName,
classLoader,
throwable.getMessage());
}
@Override

View File

@ -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);

View File

@ -1,6 +1,10 @@
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;
}
}
}
}

View File

@ -63,12 +63,12 @@ public class MuzzleVisitor implements AsmVisitorWrapper {
this.instrumentationClassName = name;
try {
instrumenter =
(Instrumenter.Default)
MuzzleVisitor.class
.getClassLoader()
.loadClass(Utils.getClassName(instrumentationClassName))
.getDeclaredConstructor()
.newInstance();
(Instrumenter.Default)
MuzzleVisitor.class
.getClassLoader()
.loadClass(Utils.getClassName(instrumentationClassName))
.getDeclaredConstructor()
.newInstance();
} catch (Exception e) {
throw new RuntimeException(e);
}
@ -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");
@ -164,11 +163,11 @@ public class MuzzleVisitor implements AsmVisitorWrapper {
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitMethodInsn(
Opcodes.INVOKEVIRTUAL,
instrumentationClassName,
"helperClassNames",
"()[Ljava/lang/String;",
false);
Opcodes.INVOKEVIRTUAL,
instrumentationClassName,
"helperClassNames",
"()[Ljava/lang/String;",
false);
final Reference[] references = generateReferences();
mv.visitIntInsn(Opcodes.BIPUSH, references.length);

View File

@ -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",
};
}

View File

@ -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 {

View File

@ -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')