Skip to content
Draft
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 @@ -50,6 +50,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 @@ -47,10 +47,29 @@ private AdviceShader(Map<String, String> relocations, List<String> helperNames)

/** Applies shading before calling the given {@link ClassVisitor}. */
public ClassVisitor shadeClass(ClassVisitor cv) {
return new ClassRemapper(cv, remapper());
}

/** Applies advice relocation rules to a binary class name. */
public String shadeClassName(String className) {
return remapper().mapType(className.replace('.', '/')).replace('/', '.');
}

/** Applies advice relocation rules to a field/type descriptor. */
public String shadeTypeDescriptor(String descriptor) {
return remapper().mapDesc(descriptor);
}

/** Applies advice relocation rules to a method descriptor. */
public String shadeMethodDescriptor(String descriptor) {
return remapper().mapMethodDesc(descriptor);
}

private Remapper remapper() {
if (null == remapper) {
remapper = new AdviceMapper();
}
return new ClassRemapper(cv, remapper);
return remapper;
}

/** Returns the result of shading the given bytecode. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ public static ReferenceMatcher loadStaticMuzzleReferences(
String muzzleClass = instrumentationClass + "$Muzzle";
try {
// Muzzle class contains static references captured at build-time
// see datadog.trace.agent.tooling.muzzle.MuzzleGenerator
// see datadog.trace.agent.tooling.muzzle.MuzzleGenerationProcessor
return (ReferenceMatcher) classLoader.loadClass(muzzleClass).getMethod("create").invoke(null);
} catch (Throwable e) {
log.warn("Failed to load - muzzle.class={}", muzzleClass, e);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package datadog.trace.agent.tooling.advice;

/** Ordered build-time consumer of a reusable {@link AdviceScanResult}. */
public interface AdviceProcessor<T> {
/** Type used to publish this processor's output to later processors. */
Class<T> resultType();

T process(AdviceScanResult scanResult, AdviceProcessorContext context);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package datadog.trace.agent.tooling.advice;

import datadog.trace.agent.tooling.InstrumenterModule;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
import net.bytebuddy.dynamic.DynamicType;

/** Mutable pipeline context shared by ordered advice processors for one module. */
public final class AdviceProcessorContext {
private final InstrumenterModule module;
private final File targetDirectory;
private final Map<Class<?>, Object> results = new HashMap<>();
private DynamicType.Builder<?> builder;

AdviceProcessorContext(
InstrumenterModule module, File targetDirectory, DynamicType.Builder<?> builder) {
this.module = module;
this.targetDirectory = targetDirectory;
this.builder = builder;
}

public InstrumenterModule getModule() {
return module;
}

public File getTargetDirectory() {
return targetDirectory;
}

public DynamicType.Builder<?> getBuilder() {
return builder;
}

public void setBuilder(DynamicType.Builder<?> builder) {
this.builder = builder;
}

public <T> T getResult(Class<T> resultType) {
Object result = results.get(resultType);
if (result == null) {
throw new IllegalStateException("No advice processor result of type " + resultType.getName());
}
return resultType.cast(result);
}

<T> void putResult(Class<T> resultType, T result) {
if (results.put(resultType, result) != null) {
throw new IllegalStateException(
"Duplicate advice processor result type " + resultType.getName());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
package datadog.trace.agent.tooling.advice;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/** Immutable, neutral result of scanning a module's method advice. */
public final class AdviceScanResult {
private static final String INSTRUMENTATION_PACKAGE = "datadog.trace.instrumentation.";

public enum UsageKind {
TYPE,
FIELD,
METHOD,
HANDLE,
INVOKEDYNAMIC
}

/** Source location of a bytecode use. */
public static final class SourceLocation {
private final String className;
private final int line;

SourceLocation(String className, int line) {
this.className = className;
this.line = line;
}

public String getClassName() {
return className;
}

public int getLine() {
return line;
}
}

/** A method-handle target without an ASM dependency in the public model. */
public static final class HandleUse {
private final int tag;
private final String owner;
private final String name;
private final String descriptor;
private final boolean interfaceOwner;

HandleUse(int tag, String owner, String name, String descriptor, boolean interfaceOwner) {
this.tag = tag;
this.owner = owner;
this.name = name;
this.descriptor = descriptor;
this.interfaceOwner = interfaceOwner;
}

public int getTag() {
return tag;
}

public String getOwner() {
return owner;
}

public String getName() {
return name;
}

public String getDescriptor() {
return descriptor;
}

public boolean isInterfaceOwner() {
return interfaceOwner;
}
}

/** One ordered bytecode use made by a scanned class. */
public static final class Usage {
private final UsageKind kind;
private final SourceLocation source;
private final int opcode;
private final String owner;
private final String name;
private final String descriptor;
private final boolean interfaceOwner;
private final boolean implementedInterface;
private final List<HandleUse> handles;

Usage(
UsageKind kind,
SourceLocation source,
int opcode,
String owner,
String name,
String descriptor,
boolean interfaceOwner,
boolean implementedInterface,
List<HandleUse> handles) {
this.kind = kind;
this.source = source;
this.opcode = opcode;
this.owner = owner;
this.name = name;
this.descriptor = descriptor;
this.interfaceOwner = interfaceOwner;
this.implementedInterface = implementedInterface;
this.handles = immutableCopy(handles);
}

public UsageKind getKind() {
return kind;
}

public SourceLocation getSource() {
return source;
}

public int getOpcode() {
return opcode;
}

/** Binary name of the used type or member owner. */
public String getOwner() {
return owner;
}

public String getName() {
return name;
}

public String getDescriptor() {
return descriptor;
}

public boolean isInterfaceOwner() {
return interfaceOwner;
}

public boolean isImplementedInterface() {
return implementedInterface;
}

public List<HandleUse> getHandles() {
return handles;
}
}

/** One discovered class. External leaves have no scanned bytecode. */
public static final class ClassInfo {
private final String className;
private final boolean scanned;
private final List<Usage> usages;

ClassInfo(String className, boolean scanned, List<Usage> usages) {
this.className = className;
this.scanned = scanned;
this.usages = immutableCopy(usages);
}

public String getClassName() {
return className;
}

public boolean isScanned() {
return scanned;
}

public boolean isInstrumentationClass() {
return AdviceScanResult.isInstrumentationClass(className);
}

public List<Usage> getUsages() {
return usages;
}
}

private final List<String> adviceRoots;
private final Map<String, ClassInfo> classes;

AdviceScanResult(Collection<String> adviceRoots, Map<String, ClassInfo> classes) {
this.adviceRoots = immutableCopy(adviceRoots);
this.classes = Collections.unmodifiableMap(new LinkedHashMap<>(classes));
}

public List<String> getAdviceRoots() {
return adviceRoots;
}

public Map<String, ClassInfo> getClasses() {
return classes;
}

public ClassInfo getClassInfo(String className) {
return classes.get(className);
}

static boolean isInstrumentationClass(String className) {
return className.startsWith(INSTRUMENTATION_PACKAGE);
}

private static <T> List<T> immutableCopy(Collection<T> values) {
return Collections.unmodifiableList(new ArrayList<>(values));
}
}
Loading