Muzzle Assertions and gradle plugin
This commit is contained in:
parent
541a6998a4
commit
628f4929dc
|
@ -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" }}
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
class MuzzleExtension {
|
||||
String group
|
||||
String module
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
implementation-class=MuzzlePlugin
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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",
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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')
|
||||
|
|
Loading…
Reference in New Issue