Skip to content

Commit 3dc6d89

Browse files
committed
Write helpers into module bytecode directly
1 parent e9b0408 commit 3dc6d89

3 files changed

Lines changed: 65 additions & 57 deletions

File tree

dd-java-agent/agent-installer/src/main/java/datadog/trace/agent/tooling/CombiningTransformerBuilder.java

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -130,12 +130,7 @@ private void prepareInstrumentation(InstrumenterModule module, int instrumentati
130130

131131
adviceShader = AdviceShader.with(module);
132132

133-
String[] helperClassNames =
134-
InstrumenterModule.loadStaticMuzzleHelperClassNames(
135-
Utils.getExtendedClassLoader(), module.getClass().getName());
136-
if (null == helperClassNames) {
137-
helperClassNames = module.helperClassNames();
138-
}
133+
String[] helperClassNames = module.helperClassNames();
139134
if (module.injectHelperDependencies()) {
140135
helperClassNames = HelperScanner.withClassDependencies(helperClassNames);
141136
}

dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/InstrumenterModule.java

Lines changed: 3 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -113,23 +113,10 @@ public static ReferenceMatcher loadStaticMuzzleReferences(
113113
}
114114

115115
/**
116-
* @return the build-time inferred and manually-declared helper class names captured by {@code
117-
* $Muzzle}, or {@code null} when none are available and fall back to {@link
118-
* #helperClassNames()}.
116+
* Optional manual additions to the injected helper set. At build time {@code MuzzleGenerator}
117+
* overwrites this with the fully resolved list (inferred + manual), so at runtime it returns
118+
* every helper the module injects.
119119
*/
120-
public static String[] loadStaticMuzzleHelperClassNames(
121-
ClassLoader classLoader, String instrumentationClass) {
122-
String muzzleClass = instrumentationClass + "$Muzzle";
123-
try {
124-
// helper class names captured at build-time; see MuzzleGenerator
125-
return (String[])
126-
classLoader.loadClass(muzzleClass).getMethod("helperClassNames").invoke(null);
127-
} catch (Throwable e) {
128-
return null;
129-
}
130-
}
131-
132-
/** Optional manual additions to the injected helper set. */
133120
public String[] helperClassNames() {
134121
return NO_HELPERS;
135122
}

dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/muzzle/MuzzleGenerator.java

Lines changed: 61 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -77,14 +77,33 @@ public ClassVisitor wrap(
7777
throw new RuntimeException(e);
7878
}
7979

80+
AdviceShader adviceShader = AdviceShader.with(module.adviceShading());
81+
82+
// Collect the muzzle references from every advice the module defines.
83+
Set<String> adviceClasses = new HashSet<>();
84+
List<Reference> allReferences = new ArrayList<>();
85+
for (Instrumenter instrumenter : module.typeInstrumentations()) {
86+
if (instrumenter instanceof Instrumenter.HasMethodAdvice) {
87+
Collections.addAll(
88+
allReferences,
89+
generateReferences(
90+
(Instrumenter.HasMethodAdvice) instrumenter, adviceShader, adviceClasses));
91+
}
92+
}
93+
94+
String[] orderedHelpers = computeInjectedHelpers(module, allReferences, adviceClasses);
95+
8096
File muzzleClass = new File(targetDir, moduleDefinition.getInternalName() + "$Muzzle.class");
8197
try {
8298
muzzleClass.getParentFile().mkdirs();
83-
Files.write(muzzleClass.toPath(), generateMuzzleClass(module));
99+
Files.write(muzzleClass.toPath(), generateMuzzleClass(module, allReferences, orderedHelpers));
84100
} catch (IOException e) {
85101
throw new RuntimeException(e);
86102
}
87-
return classVisitor;
103+
104+
// Set resolved helpers directly in the module's helperClassNames() so agent reads
105+
// them directly without loading the $Muzzle class.
106+
return new HelperClassNamesWriter(classVisitor, orderedHelpers);
88107
}
89108

90109
private static Reference[] generateReferences(
@@ -123,23 +142,8 @@ private static Reference[] generateReferences(
123142
}
124143

125144
/** This code is generated in a separate side-class. */
126-
private byte[] generateMuzzleClass(InstrumenterModule module) {
127-
128-
AdviceShader adviceShader = AdviceShader.with(module.adviceShading());
129-
130-
// Collect the muzzle references from every advice the module defines.
131-
Set<String> adviceClasses = new HashSet<>();
132-
List<Reference> allReferences = new ArrayList<>();
133-
for (Instrumenter instrumenter : module.typeInstrumentations()) {
134-
if (instrumenter instanceof Instrumenter.HasMethodAdvice) {
135-
Collections.addAll(
136-
allReferences,
137-
generateReferences(
138-
(Instrumenter.HasMethodAdvice) instrumenter, adviceShader, adviceClasses));
139-
}
140-
}
141-
142-
String[] orderedHelpers = computeInjectedHelpers(module, allReferences, adviceClasses);
145+
private byte[] generateMuzzleClass(
146+
InstrumenterModule module, List<Reference> allReferences, String[] orderedHelpers) {
143147

144148
// Injected helpers are our own classes, so they don't need to be asserted as library
145149
// references.
@@ -201,24 +205,46 @@ private byte[] generateMuzzleClass(InstrumenterModule module) {
201205
mv.visitMaxs(0, 0);
202206
mv.visitEnd();
203207

204-
// Generate helperClassNames() with resolved helpers for the agent to read at load time;
205-
// skip the method entirely when the module injects nothing.
206-
if (orderedHelpers.length > 0) {
207-
MethodVisitor hv =
208-
cw.visitMethod(
209-
Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC,
210-
"helperClassNames",
211-
"()[Ljava/lang/String;",
212-
null,
213-
null);
214-
hv.visitCode();
215-
writeStrings(hv, orderedHelpers);
216-
hv.visitInsn(Opcodes.ARETURN);
217-
hv.visitMaxs(0, 0);
218-
hv.visitEnd();
208+
return cw.toByteArray();
209+
}
210+
211+
/**
212+
* Rewrite a module's {@code helperClassNames()} to return the build-time-resolved helper list.
213+
*/
214+
private static final class HelperClassNamesWriter extends ClassVisitor {
215+
private static final String HELPER_METHOD = "helperClassNames";
216+
private static final String HELPER_DESCRIPTOR = "()[Ljava/lang/String;";
217+
218+
private final String[] helpers;
219+
220+
HelperClassNamesWriter(ClassVisitor classVisitor, String[] helpers) {
221+
super(Opcodes.ASM7, classVisitor);
222+
this.helpers = helpers;
219223
}
220224

221-
return cw.toByteArray();
225+
@Override
226+
public MethodVisitor visitMethod(
227+
int access, String name, String descriptor, String signature, String[] exceptions) {
228+
// Drop any existing helperClassNames() - resolved version will be re-added in visitEnd.
229+
if (HELPER_METHOD.equals(name) && HELPER_DESCRIPTOR.equals(descriptor)) {
230+
return null;
231+
}
232+
return super.visitMethod(access, name, descriptor, signature, exceptions);
233+
}
234+
235+
@Override
236+
public void visitEnd() {
237+
if (helpers.length > 0) {
238+
MethodVisitor mv =
239+
super.visitMethod(Opcodes.ACC_PUBLIC, HELPER_METHOD, HELPER_DESCRIPTOR, null, null);
240+
mv.visitCode();
241+
writeStrings(mv, helpers);
242+
mv.visitInsn(Opcodes.ARETURN);
243+
mv.visitMaxs(0, 0);
244+
mv.visitEnd();
245+
}
246+
super.visitEnd();
247+
}
222248
}
223249

224250
/** Resolves the ordered set of helper classes to inject for a module. */

0 commit comments

Comments
 (0)