Skip to content

Commit 3b92a69

Browse files
authored
Merge branch 'master' into mhlidd/config_inversion_plugins
2 parents c74f883 + e992b65 commit 3b92a69

33 files changed

Lines changed: 252 additions & 189 deletions

File tree

communication/src/main/java/datadog/communication/ddagent/DDAgentFeaturesDiscovery.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -271,13 +271,11 @@ private boolean processInfoResponse(State newState, String response) {
271271
newState.debuggerLogEndpoint = DEBUGGER_ENDPOINT_V1;
272272
}
273273
// both debugger v2 and diagnostics endpoints are forwarding events to the DEBUGGER intake
274-
// because older agents support diagnostics, we fall back to it before falling back to v1
274+
// because older agents support diagnostics from DD agent 7.49
275275
if (containsEndpoint(endpoints, DEBUGGER_ENDPOINT_V2)) {
276276
newState.debuggerSnapshotEndpoint = DEBUGGER_ENDPOINT_V2;
277277
} else if (containsEndpoint(endpoints, DEBUGGER_DIAGNOSTICS_ENDPOINT)) {
278278
newState.debuggerSnapshotEndpoint = DEBUGGER_DIAGNOSTICS_ENDPOINT;
279-
} else if (containsEndpoint(endpoints, DEBUGGER_ENDPOINT_V1)) {
280-
newState.debuggerSnapshotEndpoint = DEBUGGER_ENDPOINT_V1;
281279
}
282280
if (containsEndpoint(endpoints, DEBUGGER_DIAGNOSTICS_ENDPOINT)) {
283281
newState.debuggerDiagnosticsEndpoint = DEBUGGER_DIAGNOSTICS_ENDPOINT;

communication/src/test/groovy/datadog/communication/ddagent/DDAgentFeaturesDiscoveryTest.groovy

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -440,8 +440,8 @@ class DDAgentFeaturesDiscoveryTest extends DDSpecification {
440440
1 * client.newCall(_) >> { Request request -> infoResponse(request, INFO_WITH_TELEMETRY_PROXY_RESPONSE) }
441441
features.supportsTelemetryProxy()
442442
features.supportsDebugger()
443-
features.getDebuggerSnapshotEndpoint() == "debugger/v1/input"
444-
!features.supportsDebuggerDiagnostics()
443+
features.getDebuggerSnapshotEndpoint() == "debugger/v1/diagnostics"
444+
features.supportsDebuggerDiagnostics()
445445
0 * _
446446
}
447447

communication/src/test/resources/agent-features/agent-info-with-telemetry-proxy.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
"/evp_proxy/v1/",
1616
"/evp_proxy/v2/",
1717
"/debugger/v1/input",
18+
"/debugger/v1/diagnostics",
1819
"/v0.7/config"
1920
],
2021
"feature_flags": [

dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/benchmark/StaticEventLogger.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ private static String getAgentVersion() {
9797
for (int c = reader.read(); c != -1; c = reader.read()) {
9898
sb.append((char) c);
9999
}
100-
} catch (IOException e) {
100+
} catch (Throwable ignored) {
101101
// swallow exception
102102
return null;
103103
}

dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/ConfigurationUpdater.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ private void applyNewConfiguration(Configuration newConfiguration) {
131131
}
132132
currentConfiguration = newConfiguration;
133133
if (changes.hasProbeRelatedChanges()) {
134-
LOGGER.info("Applying new probe configuration, changes: {}", changes);
134+
LOGGER.debug("Applying new probe configuration, changes: {}", changes);
135135
handleProbesChanges(changes, newConfiguration);
136136
}
137137
} finally {
@@ -208,7 +208,7 @@ private void recordInstrumentationProgress(
208208
private void retransformClasses(List<Class<?>> classesToBeTransformed) {
209209
for (Class<?> clazz : classesToBeTransformed) {
210210
try {
211-
LOGGER.info("Re-transforming class: {}", clazz.getTypeName());
211+
LOGGER.debug("Re-transforming class: {}", clazz.getTypeName());
212212
instrumentation.retransformClasses(clazz);
213213
} catch (Exception ex) {
214214
ExceptionHelper.logException(LOGGER, ex, "Re-transform error:");

dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/DebuggerAgent.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -341,9 +341,11 @@ private static String getLogEndpoint(
341341
private static String getSnapshotEndpoint(
342342
Config config, DDAgentFeaturesDiscovery ddAgentFeaturesDiscovery) {
343343
if (ddAgentFeaturesDiscovery.supportsDebugger()) {
344-
return ddAgentFeaturesDiscovery
345-
.buildUrl(ddAgentFeaturesDiscovery.getDebuggerSnapshotEndpoint())
346-
.toString();
344+
String debuggerSnapshotEndpoint = ddAgentFeaturesDiscovery.getDebuggerSnapshotEndpoint();
345+
if (debuggerSnapshotEndpoint == null) {
346+
throw new IllegalArgumentException("Cannot find snapshot endpoint on datadog agent");
347+
}
348+
return ddAgentFeaturesDiscovery.buildUrl(debuggerSnapshotEndpoint).toString();
347349
}
348350
return config.getFinalDebuggerSnapshotUrl();
349351
}

dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/DebuggerTransformer.java

Lines changed: 59 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,11 @@
5959
import net.bytebuddy.description.type.TypeDescription;
6060
import net.bytebuddy.pool.TypePool;
6161
import org.objectweb.asm.ClassReader;
62+
import org.objectweb.asm.ClassVisitor;
6263
import org.objectweb.asm.ClassWriter;
64+
import org.objectweb.asm.MethodVisitor;
6365
import org.objectweb.asm.Opcodes;
66+
import org.objectweb.asm.commons.JSRInlinerAdapter;
6467
import org.objectweb.asm.tree.ClassNode;
6568
import org.objectweb.asm.tree.MethodNode;
6669
import org.objectweb.asm.tree.analysis.Analyzer;
@@ -76,7 +79,7 @@
7679
* debugger configuration
7780
*/
7881
public class DebuggerTransformer implements ClassFileTransformer {
79-
private static final Logger log = LoggerFactory.getLogger(DebuggerTransformer.class);
82+
private static final Logger LOGGER = LoggerFactory.getLogger(DebuggerTransformer.class);
8083
private static final String CANNOT_FIND_METHOD = "Cannot find method %s::%s%s";
8184
private static final String INSTRUMENTATION_FAILS = "Instrumentation fails for %s";
8285
private static final String CANNOT_FIND_LINE = "No executable code was found at %s:L%s";
@@ -148,7 +151,7 @@ public DebuggerTransformer(
148151
} else if (itwType.equals("line")) {
149152
probeCreator = this::createLineProbes;
150153
} else {
151-
log.warn(
154+
LOGGER.warn(
152155
"Invalid value for 'dd.debugger.instrument-the-world' property: {}. "
153156
+ "Valid values are 'method' or 'line'.",
154157
itwType);
@@ -202,7 +205,7 @@ private void processITWFiles(
202205
for (String fileName : fileNames) {
203206
Path excludePath = Paths.get(fileName);
204207
if (!Files.exists(excludePath)) {
205-
log.warn("Cannot find exclude file: {}", excludePath);
208+
LOGGER.warn("Cannot find exclude file: {}", excludePath);
206209
continue;
207210
}
208211
try {
@@ -223,7 +226,7 @@ private void processITWFiles(
223226
classes.add(line);
224227
});
225228
} catch (IOException ex) {
226-
log.warn("Error reading exclude file '{}' for Instrument-The-World: ", fileName, ex);
229+
LOGGER.warn("Error reading exclude file '{}' for Instrument-The-World: ", fileName, ex);
227230
}
228231
}
229232
}
@@ -250,7 +253,7 @@ public byte[] transform(
250253
if (definitions.isEmpty()) {
251254
return null;
252255
}
253-
log.debug("Matching definitions for class[{}]: {}", fullyQualifiedClassName, definitions);
256+
LOGGER.debug("Matching definitions for class[{}]: {}", fullyQualifiedClassName, definitions);
254257
if (!instrumentationIsAllowed(fullyQualifiedClassName, definitions)) {
255258
return null;
256259
}
@@ -260,22 +263,22 @@ public byte[] transform(
260263
if (transformed) {
261264
return writeClassFile(definitions, loader, classFilePath, classNode);
262265
}
263-
// This is an info log because in case of SourceFile definition and multiple top-level
266+
// We are logging this because in case of SourceFile definition and multiple top-level
264267
// classes, type may match, but there is one classfile per top-level class so source file
265268
// will match, but not the classfile.
266269
// e.g. Main.java contains Main & TopLevel class, line numbers are in TopLevel class
267-
log.info(
270+
LOGGER.debug(
268271
"type {} matched but no transformation for definitions: {}", classFilePath, definitions);
269272
} catch (Throwable ex) {
270-
log.warn("Cannot transform: ", ex);
273+
LOGGER.warn("Cannot transform: ", ex);
271274
reportInstrumentationFails(definitions, fullyQualifiedClassName);
272275
}
273276
return null;
274277
}
275278

276279
private boolean skipInstrumentation(ClassLoader loader, String classFilePath) {
277280
if (definitionMatcher.isEmpty()) {
278-
log.debug("No debugger definitions present.");
281+
LOGGER.debug("No debugger definitions present.");
279282
return true;
280283
}
281284
if (classFilePath == null) {
@@ -311,7 +314,7 @@ private byte[] transformTheWorld(
311314
location = codeSource.getLocation();
312315
}
313316
}
314-
log.debug(
317+
LOGGER.debug(
315318
"Parsing class '{}' {}B loaded from loader='{}' location={}",
316319
classFilePath,
317320
classfileBuffer.length,
@@ -320,7 +323,7 @@ private byte[] transformTheWorld(
320323
ClassNode classNode = parseClassFile(classFilePath, classfileBuffer);
321324
if (isClassLoaderRelated(classNode)) {
322325
// Skip ClassLoader classes
323-
log.debug("Skipping ClassLoader class: {}", classFilePath);
326+
LOGGER.debug("Skipping ClassLoader class: {}", classFilePath);
324327
excludeClasses.add(classFilePath);
325328
return null;
326329
}
@@ -337,10 +340,10 @@ private byte[] transformTheWorld(
337340
if (transformed) {
338341
return writeClassFile(probes, loader, classFilePath, classNode);
339342
} else {
340-
log.debug("Class not transformed: {}", classFilePath);
343+
LOGGER.debug("Class not transformed: {}", classFilePath);
341344
}
342345
} catch (Throwable ex) {
343-
log.warn("Cannot transform: ", ex);
346+
LOGGER.warn("Cannot transform: ", ex);
344347
writeToInstrumentationLog(classFilePath);
345348
}
346349
return null;
@@ -354,7 +357,7 @@ private boolean isMethodIncludedForTransformation(
354357
}
355358
String fqnMethod = classNode.name + "::" + methodNode.name;
356359
if (excludeMethods.contains(fqnMethod)) {
357-
log.debug("Skipping method: {}", fqnMethod);
360+
LOGGER.debug("Skipping method: {}", fqnMethod);
358361
return false;
359362
}
360363
return methodNames.add(methodNode.name);
@@ -404,7 +407,7 @@ private synchronized void writeToInstrumentationLog(String classFilePath) {
404407
writer.write(classFilePath);
405408
writer.write("\n");
406409
} catch (Exception ex) {
407-
log.warn("Cannot write to instrumentation.log", ex);
410+
LOGGER.warn("Cannot write to instrumentation.log", ex);
408411
}
409412
}
410413

@@ -443,7 +446,7 @@ private boolean isIncludedForTransformation(String classFilePath) {
443446
private boolean instrumentationIsAllowed(
444447
String fullyQualifiedClassName, List<ProbeDefinition> definitions) {
445448
if (denyListHelper.isDenied(fullyQualifiedClassName)) {
446-
log.info("Instrumentation denied for {}", fullyQualifiedClassName);
449+
LOGGER.debug("Instrumentation denied for {}", fullyQualifiedClassName);
447450
InstrumentationResult result =
448451
InstrumentationResult.Factory.blocked(
449452
fullyQualifiedClassName,
@@ -455,7 +458,7 @@ private boolean instrumentationIsAllowed(
455458
return false;
456459
}
457460
if (!allowListHelper.isAllowAll() && !allowListHelper.isAllowed(fullyQualifiedClassName)) {
458-
log.info("Instrumentation not allowed for {}", fullyQualifiedClassName);
461+
LOGGER.debug("Instrumentation not allowed for {}", fullyQualifiedClassName);
459462
InstrumentationResult result =
460463
InstrumentationResult.Factory.blocked(
461464
fullyQualifiedClassName,
@@ -487,11 +490,12 @@ private byte[] writeClassFile(
487490
classNode.version = Opcodes.V1_8;
488491
}
489492
ClassWriter writer = new SafeClassWriter(loader);
490-
log.debug("Generating bytecode for class: {}", Strings.getClassName(classFilePath));
493+
ClassVisitor visitor = new JsrInliningClassVisitor(writer);
494+
LOGGER.debug("Generating bytecode for class: {}", Strings.getClassName(classFilePath));
491495
try {
492-
classNode.accept(writer);
496+
classNode.accept(visitor);
493497
} catch (Throwable t) {
494-
log.error("Cannot write classfile for class: {} Exception: ", classFilePath, t);
498+
LOGGER.error("Cannot write classfile for class: {} Exception: ", classFilePath, t);
495499
reportInstrumentationFails(definitions, Strings.getClassName(classFilePath));
496500
return null;
497501
}
@@ -526,8 +530,8 @@ private void verifyByteCode(String classFilePath, byte[] classFile) {
526530
printWriter.flush();
527531
String result = stringWriter.toString();
528532
if (!result.isEmpty()) {
529-
log.warn("Verification of instrumented class {} failed", classFilePath);
530-
log.debug("Verify result: {}", stringWriter);
533+
LOGGER.warn("Verification of instrumented class {} failed", classFilePath);
534+
LOGGER.debug("Verify result: {}", stringWriter);
531535
throw new RuntimeException("Generated bytecode is invalid for " + classFilePath);
532536
}
533537
}
@@ -560,9 +564,9 @@ private boolean performInstrumentation(
560564
if (matchingDefs.isEmpty()) {
561565
continue;
562566
}
563-
if (log.isDebugEnabled()) {
567+
if (LOGGER.isDebugEnabled()) {
564568
List<String> probeIds = matchingDefs.stream().map(ProbeDefinition::getId).collect(toList());
565-
log.debug(
569+
LOGGER.debug(
566570
"Instrumenting method: {}.{}{} for probe ids: {}",
567571
fullyQualifiedClassName,
568572
methodNode.name,
@@ -627,7 +631,7 @@ private void reportErrorForAllProbes(List<ProbeDefinition> definitions, String m
627631
private void addDiagnostics(
628632
ProbeDefinition definition, List<DiagnosticMessage> diagnosticMessages) {
629633
debuggerSink.addDiagnostics(definition.getProbeId(), diagnosticMessages);
630-
log.debug("Diagnostic messages for definition[{}]: {}", definition, diagnosticMessages);
634+
LOGGER.debug("Diagnostic messages for definition[{}]: {}", definition, diagnosticMessages);
631635
}
632636

633637
private void notifyBlockedDefinitions(
@@ -658,7 +662,7 @@ private InstrumentationResult applyInstrumentation(
658662
status = definition.instrument(methodInfo, probeDiagnostics, toInstrumentInfo.probeIds);
659663
}
660664
} catch (Throwable t) {
661-
log.warn("Exception during instrumentation: ", t);
665+
LOGGER.warn("Exception during instrumentation: ", t);
662666
status = InstrumentationResult.Status.ERROR;
663667
addDiagnosticForAllProbes(
664668
new DiagnosticMessage(DiagnosticMessage.Kind.ERROR, t), diagnostics);
@@ -694,7 +698,7 @@ private List<ToInstrumentInfo> filterAndSortDefinitions(
694698
// and therefore need to be instrumented once
695699
// note: exception probes are log probes and are handled the same way
696700
if (!Config.get().isDistributedDebuggerEnabled() && definition instanceof TriggerProbe) {
697-
log.debug(
701+
LOGGER.debug(
698702
"The distributed debugger feature is disabled. Trigger probes will not be installed.");
699703
} else if (isCapturedContextProbe(definition)) {
700704
if (definition.isLineProbe()) {
@@ -871,7 +875,7 @@ private List<MethodNode> matchMethodDescription(
871875
}
872876
}
873877
} catch (Exception ex) {
874-
log.warn("Cannot match method: {}", ex.toString());
878+
LOGGER.warn("Cannot match method: {}", ex.toString());
875879
}
876880
return result;
877881
}
@@ -888,22 +892,22 @@ private MethodNode matchSourceFile(
888892
if (matchingMethods != null) {
889893
matchingMethods.forEach(
890894
methodNode -> {
891-
log.debug("Found lineNode {} method: {}", matchingLine, methodNode.name);
895+
LOGGER.debug("Found lineNode {} method: {}", matchingLine, methodNode.name);
892896
});
893897
// pick the first matching method.
894898
// TODO need a way to disambiguate if multiple methods match the same line
895899
return matchingMethods.isEmpty() ? null : matchingMethods.get(0);
896900
}
897-
log.debug("Cannot find line: {} in class {}", matchingLine, classNode.name);
901+
LOGGER.debug("Cannot find line: {} in class {}", matchingLine, classNode.name);
898902
return null;
899903
}
900904

901905
private void dumpInstrumentedClassFile(String className, byte[] data) {
902906
if (config.isDynamicInstrumentationClassFileDumpEnabled()) {
903-
log.debug("Generated bytecode len: {}", data.length);
907+
LOGGER.debug("Generated bytecode len: {}", data.length);
904908
Path classFilePath = dumpClassFile(className, data);
905909
if (classFilePath != null) {
906-
log.debug("Instrumented class saved as: {}", classFilePath.toString());
910+
LOGGER.debug("Instrumented class saved as: {}", classFilePath.toString());
907911
}
908912
}
909913
}
@@ -912,7 +916,7 @@ private void dumpOriginalClassFile(String className, byte[] classfileBuffer) {
912916
if (config.isDynamicInstrumentationClassFileDumpEnabled()) {
913917
Path classFilePath = dumpClassFile(className + "_orig", classfileBuffer);
914918
if (classFilePath != null) {
915-
log.debug("Original class saved as: {}", classFilePath.toString());
919+
LOGGER.debug("Original class saved as: {}", classFilePath.toString());
916920
}
917921
}
918922
}
@@ -924,11 +928,31 @@ private static Path dumpClassFile(String className, byte[] classfileBuffer) {
924928
Files.write(classFilePath, classfileBuffer, StandardOpenOption.CREATE);
925929
return classFilePath;
926930
} catch (IOException e) {
927-
log.error("", e);
931+
LOGGER.error("", e);
928932
return null;
929933
}
930934
}
931935

936+
/**
937+
* A {@link org.objectweb.asm.ClassVisitor} that uses {@link
938+
* org.objectweb.asm.commons.JSRInlinerAdapter} to remove JSR instructions and inlines the
939+
* referenced subroutines. This allows pre-Java 6 classes with finally blocks to be successfully
940+
* transformed. Without this an IllegalArgumentException for "JSR/RET are not supported with
941+
* computeFrames option" would be thrown when writing the transformed class.
942+
*/
943+
static class JsrInliningClassVisitor extends ClassVisitor {
944+
protected JsrInliningClassVisitor(ClassVisitor parent) {
945+
super(Opcodes.ASM9, parent);
946+
}
947+
948+
@Override
949+
public MethodVisitor visitMethod(
950+
int access, String name, String descriptor, String signature, String[] exceptions) {
951+
MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);
952+
return new JSRInlinerAdapter(mv, access, name, descriptor, signature, exceptions);
953+
}
954+
}
955+
932956
static class SafeClassWriter extends ClassWriter {
933957
private final ClassLoader classLoader;
934958

@@ -981,7 +1005,7 @@ protected String getCommonSuperClass(String type1, String type2) {
9811005
}
9821006
return common.getInternalName();
9831007
} catch (Exception ex) {
984-
ExceptionHelper.logException(log, ex, "getCommonSuperClass failed: ");
1008+
ExceptionHelper.logException(LOGGER, ex, "getCommonSuperClass failed: ");
9851009
return tpDatadogClassLoader.describe("java.lang.Object").resolve().getInternalName();
9861010
}
9871011
}

dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/codeorigin/DefaultCodeOriginRecorder.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,10 @@ public DefaultCodeOriginRecorder(
6060

6161
@Override
6262
public String captureCodeOrigin(boolean entry) {
63+
if (!entry) {
64+
LOG.debug("Not capturing code origin for exit");
65+
return null;
66+
}
6367
StackTraceElement element = findPlaceInStack();
6468
String fingerprint = Fingerprinter.fingerprint(element);
6569
CodeOriginProbe probe = probesByFingerprint.get(fingerprint);

0 commit comments

Comments
 (0)