diff --git a/dd-java-agent/agent-debugger/debugger-bootstrap/src/main/java/datadog/trace/bootstrap/debugger/DebuggerContext.java b/dd-java-agent/agent-debugger/debugger-bootstrap/src/main/java/datadog/trace/bootstrap/debugger/DebuggerContext.java index 663cd88033f..9d1c793ea8e 100644 --- a/dd-java-agent/agent-debugger/debugger-bootstrap/src/main/java/datadog/trace/bootstrap/debugger/DebuggerContext.java +++ b/dd-java-agent/agent-debugger/debugger-bootstrap/src/main/java/datadog/trace/bootstrap/debugger/DebuggerContext.java @@ -7,6 +7,7 @@ import java.time.Duration; import java.time.temporal.ChronoUnit; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -337,6 +338,18 @@ public static void evalContextAndCommit( } } + public static void codeOrigin(String probeId) { + try { + ProbeImplementation probe = probeResolver.resolve(probeId); + if (probe != null) { + probe.commit( + CapturedContext.EMPTY_CONTEXT, CapturedContext.EMPTY_CONTEXT, Collections.emptyList()); + } + } catch (Exception e) { + LOGGER.debug("Error in codeOrigin: ", e); + } + } + /** * Commit snapshot based on entry/exit contexts and eventually caught exceptions for given probe * Ids This is for method probes diff --git a/dd-java-agent/agent-debugger/debugger-bootstrap/src/main/java/datadog/trace/bootstrap/debugger/ProbeId.java b/dd-java-agent/agent-debugger/debugger-bootstrap/src/main/java/datadog/trace/bootstrap/debugger/ProbeId.java index c5c0eeb7229..d00bb4f0ef7 100644 --- a/dd-java-agent/agent-debugger/debugger-bootstrap/src/main/java/datadog/trace/bootstrap/debugger/ProbeId.java +++ b/dd-java-agent/agent-debugger/debugger-bootstrap/src/main/java/datadog/trace/bootstrap/debugger/ProbeId.java @@ -1,5 +1,7 @@ package datadog.trace.bootstrap.debugger; +import java.util.UUID; + public class ProbeId { private static final String ID_SEPARATOR = ":"; @@ -29,6 +31,10 @@ private ProbeId(String id, int version, String encoded) { this.encoded = encoded; } + public static ProbeId newId() { + return new ProbeId(UUID.randomUUID().toString(), 0); + } + public String getId() { return id; } diff --git a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/Configuration.java b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/Configuration.java index fae9f2a099a..d0676ef35d6 100644 --- a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/Configuration.java +++ b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/Configuration.java @@ -1,6 +1,5 @@ package com.datadog.debugger.agent; -import com.datadog.debugger.probe.ExceptionProbe; import com.datadog.debugger.probe.LogProbe; import com.datadog.debugger.probe.MetricProbe; import com.datadog.debugger.probe.ProbeDefinition; @@ -12,6 +11,8 @@ import java.util.Collection; import java.util.List; import java.util.Objects; +import java.util.stream.Collectors; +import java.util.stream.Stream; /** * Stores debugger configuration for a service with: - Probe definitions - filters (allow/deny) - @@ -63,46 +64,47 @@ public int hashCode() { @Json(name = "id") private final String service; - private final Collection metricProbes; - private final Collection logProbes; - private final Collection spanProbes; - private final Collection triggerProbes; - private final Collection spanDecorationProbes; + private transient List probes = new ArrayList<>(); + private Collection metricProbes = new ArrayList<>(); + private Collection logProbes = new ArrayList<>(); + private Collection spanProbes = new ArrayList<>(); + private Collection triggerProbes = new ArrayList<>(); + private Collection spanDecorationProbes = new ArrayList<>(); private final FilterList allowList; private final FilterList denyList; private final LogProbe.Sampling sampling; - public Configuration(String service, Collection logProbes) { - this(service, null, logProbes, null); + public Configuration(String serviceName, List probes) { + this(serviceName, probes, null, null, null); } public Configuration( String serviceName, - Collection metricProbes, - Collection logProbes, - Collection spanProbes) { - this(serviceName, metricProbes, logProbes, spanProbes, null, null, null, null, null); - } - - public Configuration( - String serviceName, - Collection metricProbes, - Collection logProbes, - Collection spanProbes, - Collection triggerProbes, - Collection spanDecorationProbes, + List probes, FilterList allowList, FilterList denyList, LogProbe.Sampling sampling) { this.service = serviceName; - this.metricProbes = metricProbes; - this.logProbes = logProbes; - this.spanProbes = spanProbes; - this.triggerProbes = triggerProbes; - this.spanDecorationProbes = spanDecorationProbes; this.allowList = allowList; this.denyList = denyList; this.sampling = sampling; + probes.forEach(this::add); + } + + private void add(ProbeDefinition p) { + if (p instanceof LogProbe) { + logProbes.add((LogProbe) p); + } else if (p instanceof MetricProbe) { + metricProbes.add((MetricProbe) p); + } else if (p instanceof SpanProbe) { + spanProbes.add((SpanProbe) p); + } else if (p instanceof SpanDecorationProbe) { + spanDecorationProbes.add((SpanDecorationProbe) p); + } else if (p instanceof TriggerProbe) { + triggerProbes.add((TriggerProbe) p); + } else { + probes.add(p); + } } public String getService() { @@ -141,24 +143,12 @@ public LogProbe.Sampling getSampling() { return sampling; } - public Collection getDefinitions() { - Collection result = new ArrayList<>(); - if (triggerProbes != null) { - result.addAll(triggerProbes); - } - if (metricProbes != null) { - result.addAll(metricProbes); - } - if (logProbes != null) { - result.addAll(logProbes); - } - if (spanProbes != null) { - result.addAll(spanProbes); - } - if (spanDecorationProbes != null) { - result.addAll(spanDecorationProbes); - } - return result; + public List getDefinitions() { + return Stream.of( + triggerProbes, metricProbes, logProbes, spanProbes, spanDecorationProbes, probes) + .filter(Objects::nonNull) + .flatMap(Collection::stream) + .collect(Collectors.toList()); } @Generated @@ -167,10 +157,8 @@ public String toString() { return "DebuggerConfiguration{" + "service=" + service - + ", metricProbes=" - + metricProbes - + ", logProbes=" - + logProbes + + ", probes=" + + getDefinitions() + ", allowList=" + allowList + ", denyList=" @@ -183,12 +171,15 @@ public String toString() { @Generated @Override public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } Configuration that = (Configuration) o; return Objects.equals(service, that.service) - && Objects.equals(metricProbes, that.metricProbes) - && Objects.equals(logProbes, that.logProbes) + && Objects.equals(probes, that.probes) && Objects.equals(allowList, that.allowList) && Objects.equals(denyList, that.denyList) && Objects.equals(sampling, that.sampling); @@ -197,7 +188,7 @@ public boolean equals(Object o) { @Generated @Override public int hashCode() { - return Objects.hash(service, metricProbes, logProbes, allowList, denyList, sampling); + return Objects.hash(service, probes, allowList, denyList, sampling); } public static Configuration.Builder builder() { @@ -206,13 +197,13 @@ public static Configuration.Builder builder() { public static class Builder { private String service = null; - private List metricProbes = null; - private List logProbes = null; - private List spanProbes = null; - private List triggerProbes = null; - private List spanDecorationProbes = null; + + private final List probes = new ArrayList<>(); + private FilterList allowList = null; + private FilterList denyList = null; + private LogProbe.Sampling sampling = null; public Configuration.Builder setService(String service) { @@ -224,53 +215,14 @@ public Configuration.Builder add(Collection definitio if (definitions == null) { return this; } - for (ProbeDefinition definition : definitions) { - if (definition instanceof MetricProbe) add((MetricProbe) definition); - if (definition instanceof TriggerProbe) add((TriggerProbe) definition); - if (definition instanceof LogProbe) add((LogProbe) definition); - if (definition instanceof SpanProbe) add((SpanProbe) definition); - if (definition instanceof SpanDecorationProbe) add((SpanDecorationProbe) definition); - } + probes.addAll(definitions); return this; } - public Configuration.Builder add(MetricProbe probe) { - if (metricProbes == null) { - metricProbes = new ArrayList<>(); + public Configuration.Builder add(ProbeDefinition... probes) { + for (ProbeDefinition probe : probes) { + this.probes.add(probe); } - metricProbes.add(probe); - return this; - } - - public Configuration.Builder add(LogProbe probe) { - if (logProbes == null) { - logProbes = new ArrayList<>(); - } - logProbes.add(probe); - return this; - } - - public Configuration.Builder add(SpanProbe probe) { - if (spanProbes == null) { - spanProbes = new ArrayList<>(); - } - spanProbes.add(probe); - return this; - } - - public Configuration.Builder add(TriggerProbe probe) { - if (triggerProbes == null) { - triggerProbes = new ArrayList<>(); - } - triggerProbes.add(probe); - return this; - } - - public Configuration.Builder add(SpanDecorationProbe probe) { - if (spanDecorationProbes == null) { - spanDecorationProbes = new ArrayList<>(); - } - spanDecorationProbes.add(probe); return this; } @@ -281,66 +233,6 @@ public Configuration.Builder add(LogProbe.Sampling newSampling) { return this; } - public Configuration.Builder addMetricProbes(Collection probes) { - if (probes == null) { - return this; - } - for (MetricProbe probe : probes) { - add(probe); - } - return this; - } - - public Configuration.Builder addLogProbes(Collection probes) { - if (probes == null) { - return this; - } - for (LogProbe probe : probes) { - add(probe); - } - return this; - } - - public Builder addExceptionProbes(Collection probes) { - if (probes == null) { - return this; - } - for (ExceptionProbe probe : probes) { - add(probe); - } - return this; - } - - public Configuration.Builder addSpanProbes(Collection probes) { - if (probes == null) { - return this; - } - for (SpanProbe probe : probes) { - add(probe); - } - return this; - } - - public Configuration.Builder addTriggerProbes(Collection probes) { - if (probes == null) { - return this; - } - for (TriggerProbe probe : probes) { - add(probe); - } - return this; - } - - public Configuration.Builder addSpanDecorationProbes(Collection probes) { - if (probes == null) { - return this; - } - for (SpanDecorationProbe probe : probes) { - add(probe); - } - return this; - } - public Configuration.Builder addAllowList(FilterList newAllowList) { if (newAllowList == null) { return this; @@ -370,32 +262,8 @@ public Configuration.Builder setSampling(LogProbe.Sampling sampling) { return this; } - public Configuration.Builder add(Configuration other) { - if (other.service != null) { - this.service = other.service; - } - addMetricProbes(other.getMetricProbes()); - addLogProbes(other.getLogProbes()); - addSpanProbes(other.getSpanProbes()); - addTriggerProbes(other.getTriggerProbes()); - addSpanDecorationProbes(other.getSpanDecorationProbes()); - addAllowList(other.getAllowList()); - addDenyList(other.getDenyList()); - add(other.getSampling()); - return this; - } - public Configuration build() { - return new Configuration( - service, - metricProbes, - logProbes, - spanProbes, - triggerProbes, - spanDecorationProbes, - allowList, - denyList, - sampling); + return new Configuration(service, probes, allowList, denyList, sampling); } } } diff --git a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/DebuggerTransformer.java b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/DebuggerTransformer.java index 74591842a7f..7295bfd4efb 100644 --- a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/DebuggerTransformer.java +++ b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/DebuggerTransformer.java @@ -87,6 +87,8 @@ public class DebuggerTransformer implements ClassFileTransformer { SpanProbe.class); private static final String JAVA_IO_TMPDIR = "java.io.tmpdir"; + public static Path DUMP_PATH = Paths.get(System.getProperty(JAVA_IO_TMPDIR), "debugger"); + private final Config config; private final TransformerDefinitionMatcher definitionMatcher; private final AllowListHelper allowListHelper; @@ -517,7 +519,6 @@ private boolean performInstrumentation( private void handleInstrumentationResult( List definitions, InstrumentationResult result) { for (ProbeDefinition definition : definitions) { - definition.buildLocation(result); if (listener != null) { listener.instrumentationResult(definition, result); } @@ -578,7 +579,10 @@ private InstrumentationResult applyInstrumentation( MethodInfo methodInfo, List definitions) { Map> diagnostics = new HashMap<>(); definitions.forEach( - probeDefinition -> diagnostics.put(probeDefinition.getProbeId(), new ArrayList<>())); + probeDefinition -> { + probeDefinition.buildLocation(methodInfo); + diagnostics.put(probeDefinition.getProbeId(), new ArrayList<>()); + }); InstrumentationResult.Status status = preCheckInstrumentation(diagnostics, methodInfo); if (status != InstrumentationResult.Status.ERROR) { try { @@ -842,8 +846,7 @@ private void dumpOriginalClassFile(String className, byte[] classfileBuffer) { private static Path dumpClassFile(String className, byte[] classfileBuffer) { try { - Path classFilePath = - Paths.get(System.getProperty(JAVA_IO_TMPDIR), "debugger", className + ".class"); + Path classFilePath = DUMP_PATH.resolve(className + ".class"); Files.createDirectories(classFilePath.getParent()); Files.write(classFilePath, classfileBuffer, StandardOpenOption.CREATE); return classFilePath; diff --git a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/codeorigin/DebuggerConfiguration.java b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/codeorigin/DebuggerConfiguration.java deleted file mode 100644 index 8be9c8e3c75..00000000000 --- a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/codeorigin/DebuggerConfiguration.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.datadog.debugger.codeorigin; - -import datadog.trace.bootstrap.instrumentation.api.AgentSpan; -import datadog.trace.core.DDSpanContext; -import datadog.trace.core.propagation.PropagationTags; - -public final class DebuggerConfiguration { - public static boolean isDebuggerEnabled(AgentSpan span) { - return "1".equals(getDebugLevel(span)); - } - - private static String getDebugLevel(AgentSpan span) { - PropagationTags tags = ((DDSpanContext) span.getLocalRootSpan().context()).getPropagationTags(); - return tags.getDebugPropagation(); - } -} diff --git a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/codeorigin/DefaultCodeOriginRecorder.java b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/codeorigin/DefaultCodeOriginRecorder.java index 0f53e64ed0b..1cd7f4337d2 100644 --- a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/codeorigin/DefaultCodeOriginRecorder.java +++ b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/codeorigin/DefaultCodeOriginRecorder.java @@ -1,11 +1,17 @@ package com.datadog.debugger.codeorigin; import static com.datadog.debugger.agent.ConfigurationAcceptor.Source.CODE_ORIGIN; +import static datadog.trace.api.DDTags.DD_CODE_ORIGIN_FRAME; +import static java.lang.String.format; import com.datadog.debugger.agent.ConfigurationUpdater; import com.datadog.debugger.exception.Fingerprinter; import com.datadog.debugger.probe.CodeOriginProbe; +import com.datadog.debugger.probe.LogProbe; +import com.datadog.debugger.probe.LogProbe.Builder; +import com.datadog.debugger.probe.ProbeDefinition; import com.datadog.debugger.probe.Where; +import com.datadog.debugger.sink.Snapshot; import datadog.trace.api.Config; import datadog.trace.bootstrap.debugger.CapturedContext; import datadog.trace.bootstrap.debugger.DebuggerContext; @@ -19,9 +25,12 @@ import java.util.Collection; import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Map; -import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Consumer; +import java.util.stream.Collectors; +import java.util.stream.Stream; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -33,12 +42,21 @@ public class DefaultCodeOriginRecorder implements CodeOriginRecorder { private final Map probesByFingerprint = new HashMap<>(); private final Map probes = new ConcurrentHashMap<>(); + private final Map logProbes = new ConcurrentHashMap<>(); private final int maxUserFrames; + private AgentTaskScheduler scheduler = AgentTaskScheduler.INSTANCE; + public DefaultCodeOriginRecorder(Config config, ConfigurationUpdater configurationUpdater) { + this(config, configurationUpdater, AgentTaskScheduler.INSTANCE); + } + + public DefaultCodeOriginRecorder( + Config config, ConfigurationUpdater configurationUpdater, AgentTaskScheduler scheduler) { this.configurationUpdater = configurationUpdater; maxUserFrames = config.getDebuggerCodeOriginMaxUserFrames(); + this.scheduler = scheduler; } @Override @@ -54,6 +72,7 @@ public String captureCodeOrigin(boolean entry) { null, String.valueOf(element.getLineNumber())); probe = createProbe(fingerprint, entry, where); + LOG.debug("Creating probe for location {}", where); } return probe.getId(); @@ -70,16 +89,37 @@ public String captureCodeOrigin(Method method, boolean entry) { return probe.getId(); } + public void registerLogProbe(CodeOriginProbe probe) { + LogProbe logProbe = + logProbes.computeIfAbsent( + probe.getId(), + key -> + new Builder() + .language(probe.getLanguage()) + .probeId(ProbeId.newId()) + .where(probe.getWhere()) + .evaluateAt(probe.getEvaluateAt()) + .captureSnapshot(true) + .tags("session_id:*") + .snapshotProcessor(new CodeOriginSnapshotConsumer(probe.entrySpanProbe())) + .build()); + } + private CodeOriginProbe createProbe(String fingerPrint, boolean entry, Where where) { CodeOriginProbe probe; AgentSpan span = AgentTracer.activeSpan(); - probe = - new CodeOriginProbe( - new ProbeId(UUID.randomUUID().toString(), 0), entry, where, maxUserFrames); + probe = new CodeOriginProbe(ProbeId.newId(), entry, where); addFingerprint(fingerPrint, probe); + CodeOriginProbe installed = probes.putIfAbsent(probe.getId(), probe); - installProbe(probe); + // i think this check is unnecessary at this point time but leaving for now to be safe + if (installed == null) { + if (Config.get().isDebuggerEnabled()) { + registerLogProbe(probe); + } + installProbes(); + } // committing here manually so that first run probe encounters decorate the span until the // instrumentation gets installed if (span != null) { @@ -102,19 +142,35 @@ void addFingerprint(String fingerprint, CodeOriginProbe probe) { probesByFingerprint.putIfAbsent(fingerprint, probe); } - public void installProbe(CodeOriginProbe probe) { - CodeOriginProbe installed = probes.putIfAbsent(probe.getId(), probe); - if (installed == null) { - AgentTaskScheduler.INSTANCE.execute( - () -> configurationUpdater.accept(CODE_ORIGIN, getProbes())); - } + public void installProbes() { + scheduler.execute(() -> configurationUpdater.accept(CODE_ORIGIN, getProbes())); } public CodeOriginProbe getProbe(String probeId) { return probes.get(probeId); } - public Collection getProbes() { - return probes.values(); + public List getProbes() { + return Stream.of(probes.values(), logProbes.values()) + .flatMap(Collection::stream) + .collect(Collectors.toList()); + } + + private static class CodeOriginSnapshotConsumer implements Consumer { + private final boolean entrySpanProbe; + + public CodeOriginSnapshotConsumer(boolean entrySpanProbe) { + this.entrySpanProbe = entrySpanProbe; + } + + @Override + public void accept(Snapshot snapshot) { + AgentSpan span = AgentTracer.get().activeSpan(); + String snapshotId = format(DD_CODE_ORIGIN_FRAME, 0, "snapshot_id"); + span.setTag(snapshotId, snapshot.getId()); + if (entrySpanProbe) { + span.getLocalRootSpan().setTag(snapshotId, snapshot.getId()); + } + } } } diff --git a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/instrumentation/CodeOriginInstrumentor.java b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/instrumentation/CodeOriginInstrumentor.java new file mode 100644 index 00000000000..f8e94b3baec --- /dev/null +++ b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/instrumentation/CodeOriginInstrumentor.java @@ -0,0 +1,36 @@ +package com.datadog.debugger.instrumentation; + +import static com.datadog.debugger.instrumentation.ASMHelper.invokeStatic; +import static com.datadog.debugger.instrumentation.ASMHelper.ldc; +import static com.datadog.debugger.instrumentation.Types.DEBUGGER_CONTEXT_TYPE; +import static com.datadog.debugger.instrumentation.Types.STRING_TYPE; + +import com.datadog.debugger.instrumentation.InstrumentationResult.Status; +import com.datadog.debugger.probe.ProbeDefinition; +import datadog.trace.bootstrap.debugger.ProbeId; +import java.util.List; +import org.objectweb.asm.Type; +import org.objectweb.asm.tree.InsnList; + +public class CodeOriginInstrumentor extends Instrumentor { + public CodeOriginInstrumentor( + ProbeDefinition definition, + MethodInfo methodInfo, + List diagnostics, + List probeIds) { + super(definition, methodInfo, diagnostics, probeIds); + } + + @Override + public Status instrument() { + InsnList insnList = new InsnList(); + + ldc(insnList, probeIds.get(0).getEncodedId()); + + invokeStatic(insnList, DEBUGGER_CONTEXT_TYPE, "codeOrigin", Type.VOID_TYPE, STRING_TYPE); + + methodNode.instructions.insert(methodEnterLabel, insnList); + + return InstrumentationResult.Status.INSTALLED; + } +} diff --git a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/instrumentation/InstrumentationResult.java b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/instrumentation/InstrumentationResult.java index 58817ebebf1..45b0161984a 100644 --- a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/instrumentation/InstrumentationResult.java +++ b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/instrumentation/InstrumentationResult.java @@ -22,10 +22,11 @@ public enum Status { private final String typeName; private final String methodName; private final int methodStart; + private final String signature; public static class Factory { public static InstrumentationResult blocked(String className) { - return new InstrumentationResult(Status.BLOCKED, null, className, null); + return new InstrumentationResult(Status.BLOCKED, className, null); } public static InstrumentationResult blocked( @@ -34,32 +35,23 @@ public static InstrumentationResult blocked( definitions.forEach( probeDefinition -> diagnostics.put(probeDefinition.getProbeId(), Arrays.asList(messages))); - return new InstrumentationResult(Status.BLOCKED, diagnostics, className, null); + return new InstrumentationResult(Status.BLOCKED, diagnostics, null, className, null); } } public InstrumentationResult( Status status, Map> diagnostics, MethodInfo methodInfo) { - this( - status, - diagnostics, - methodInfo.getClassNode().sourceFile, - methodInfo.getClassNode().name.replace('/', '.'), - methodInfo.getMethodNode().name, - methodInfo.getMethodStart()); - } - - public InstrumentationResult( - Status status, - Map> diagnostics, - String className, - String methodName) { this.status = status; this.diagnostics = diagnostics; - this.typeName = className; - this.methodName = methodName; - this.methodStart = -1; - this.sourceFileName = null; + this.sourceFileName = methodInfo.getSourceFileName(); + this.typeName = methodInfo.getTypeName(); + this.methodName = methodInfo.getMethodName(); + this.methodStart = methodInfo.getMethodStart(); + this.signature = methodInfo.getSignature(); + } + + public InstrumentationResult(Status status, String className, String methodName) { + this(status, null, className, className, methodName); } public InstrumentationResult( @@ -67,14 +59,14 @@ public InstrumentationResult( Map> diagnostics, String sourceFileName, String className, - String methodName, - int methodStart) { + String methodName) { this.status = status; this.diagnostics = diagnostics; this.sourceFileName = sourceFileName; this.typeName = className; this.methodName = methodName; - this.methodStart = methodStart; + this.methodStart = -1; + this.signature = null; } public boolean isError() { @@ -109,20 +101,16 @@ public String getSourceFileName() { return sourceFileName; } + public String getMethodSignature() { + return signature; + } + @Generated @Override public String toString() { - return "InstrumentationResult{" - + "status=" - + status - + ", diagnostics=" - + diagnostics - + ", typeName='" - + typeName - + '\'' - + ", methodName='" - + methodName - + '\'' - + '}'; + return String.format( + "InstrumentationResult{typeName='%s', methodName='%s', methodStart=%d, signature='%s'," + + " sourceFileName='%s', status=%s. diagnostics=%s}", + typeName, methodName, methodStart, signature, sourceFileName, status, diagnostics); } } diff --git a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/instrumentation/MethodInfo.java b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/instrumentation/MethodInfo.java index 085e1b1510c..3ae5d5568a9 100644 --- a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/instrumentation/MethodInfo.java +++ b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/instrumentation/MethodInfo.java @@ -1,6 +1,7 @@ package com.datadog.debugger.instrumentation; import com.datadog.debugger.util.ClassFileLines; +import datadog.trace.util.Strings; import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.MethodNode; @@ -30,6 +31,10 @@ public ClassNode getClassNode() { return classNode; } + public String getMethodName() { + return methodNode.name; + } + public MethodNode getMethodNode() { return methodNode; } @@ -41,4 +46,16 @@ public ClassFileLines getClassFileLines() { public int getMethodStart() { return classFileLines != null ? classFileLines.getMethodStart(methodNode) : -1; } + + public String getSignature() { + return methodNode.desc != null ? Types.descriptorToSignature(methodNode.desc) : null; + } + + public String getSourceFileName() { + return classNode.sourceFile; + } + + public String getTypeName() { + return Strings.getClassName(classNode.name); + } } diff --git a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/CodeOriginProbe.java b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/CodeOriginProbe.java index f9f618cd922..8e2ca9faadf 100644 --- a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/CodeOriginProbe.java +++ b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/CodeOriginProbe.java @@ -1,176 +1,92 @@ package com.datadog.debugger.probe; -import static com.datadog.debugger.codeorigin.DebuggerConfiguration.isDebuggerEnabled; import static datadog.trace.api.DDTags.DD_CODE_ORIGIN_FRAME; import static datadog.trace.api.DDTags.DD_CODE_ORIGIN_TYPE; import static java.lang.String.format; import static java.util.Arrays.asList; import static java.util.Collections.singletonList; -import com.datadog.debugger.agent.DebuggerAgent; -import com.datadog.debugger.instrumentation.InstrumentationResult; -import com.datadog.debugger.sink.DebuggerSink; -import com.datadog.debugger.sink.Snapshot; +import com.datadog.debugger.instrumentation.CodeOriginInstrumentor; +import com.datadog.debugger.instrumentation.DiagnosticMessage; +import com.datadog.debugger.instrumentation.InstrumentationResult.Status; +import com.datadog.debugger.instrumentation.MethodInfo; import datadog.trace.bootstrap.debugger.CapturedContext; -import datadog.trace.bootstrap.debugger.DebuggerContext; +import datadog.trace.bootstrap.debugger.CapturedContext.CapturedThrowable; import datadog.trace.bootstrap.debugger.MethodLocation; import datadog.trace.bootstrap.debugger.ProbeId; import datadog.trace.bootstrap.debugger.ProbeLocation; import datadog.trace.bootstrap.instrumentation.api.AgentSpan; import datadog.trace.bootstrap.instrumentation.api.AgentTracer; -import datadog.trace.util.stacktrace.StackWalkerFactory; import java.util.List; -import java.util.stream.Collectors; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class CodeOriginProbe extends LogProbe implements ForceMethodInstrumentation { +public class CodeOriginProbe extends ProbeDefinition { private static final Logger LOGGER = LoggerFactory.getLogger(CodeOriginProbe.class); - public final int maxFrames; - private final boolean entrySpanProbe; - private List stackTraceElements; + private String signature; - public CodeOriginProbe(ProbeId probeId, boolean entry, Where where, int maxFrames) { - super(LANGUAGE, probeId, null, where, MethodLocation.EXIT, null, null, true, null, null, null); + public CodeOriginProbe(ProbeId probeId, boolean entry, Where where) { + super(LANGUAGE, probeId, (Tag[]) null, where, MethodLocation.ENTRY); this.entrySpanProbe = entry; - this.maxFrames = maxFrames; - } - - @Override - public boolean isLineProbe() { - // these are always method probes even if there is a line number - return false; } @Override - public void evaluate( - CapturedContext context, CapturedContext.Status status, MethodLocation methodLocation) { - if (!MethodLocation.isSame(methodLocation, getEvaluateAt())) { - return; - } - - super.evaluate(context, status, methodLocation); - AgentSpan span = findSpan(AgentTracer.activeSpan()); - - if (span == null) { - LOGGER.debug("Could not find the span for probeId {}", id); - return; - } - applyCodeOriginTags(span); + public Status instrument( + MethodInfo methodInfo, List diagnostics, List probeIds) { + return new CodeOriginInstrumentor(this, methodInfo, diagnostics, probeIds).instrument(); } @Override public void commit( CapturedContext entryContext, CapturedContext exitContext, - List caughtExceptions) { - - AgentSpan span = findSpan(AgentTracer.activeSpan()); - + List caughtExceptions) { + AgentSpan span = AgentTracer.activeSpan(); if (span == null) { LOGGER.debug("Could not find the span for probeId {}", id); return; } - DebuggerSink sink = DebuggerAgent.getSink(); - if (isDebuggerEnabled(span) && sink != null) { - Snapshot snapshot = createSnapshot(); - if (fillSnapshot(entryContext, exitContext, caughtExceptions, snapshot)) { - String snapshotId = snapshot.getId(); - LOGGER.debug("committing code origin probe id={}, snapshot id={}", id, snapshotId); - commitSnapshot(snapshot, sink); - - List agentSpans = - entrySpanProbe ? asList(span, span.getLocalRootSpan()) : singletonList(span); - for (AgentSpan agentSpan : agentSpans) { - if (agentSpan.getTag(format(DD_CODE_ORIGIN_FRAME, 0, "snapshot_id")) == null) { - agentSpan.setTag(format(DD_CODE_ORIGIN_FRAME, 0, "snapshot_id"), snapshotId); - } - } - } - } - if (sink != null) { - sink.getProbeStatusSink().addEmitting(probeId); - } - span.getLocalRootSpan().setTag(getId(), (String) null); // clear possible span reference - } - - private List applyCodeOriginTags(AgentSpan span) { - List entries = - stackTraceElements != null ? stackTraceElements : getUserStackFrames(); List agentSpans = entrySpanProbe ? asList(span, span.getLocalRootSpan()) : singletonList(span); for (AgentSpan s : agentSpans) { if (s.getTag(DD_CODE_ORIGIN_TYPE) == null) { s.setTag(DD_CODE_ORIGIN_TYPE, entrySpanProbe ? "entry" : "exit"); - - for (int i = 0; i < entries.size(); i++) { - StackTraceElement info = entries.get(i); - String fileName = info.getFileName(); - s.setTag(format(DD_CODE_ORIGIN_FRAME, i, "file"), fileName); - s.setTag(format(DD_CODE_ORIGIN_FRAME, i, "method"), info.getMethodName()); - s.setTag(format(DD_CODE_ORIGIN_FRAME, i, "line"), info.getLineNumber()); - s.setTag(format(DD_CODE_ORIGIN_FRAME, i, "type"), info.getClassName()); - if (i == 0 && entrySpanProbe) { - s.setTag(format(DD_CODE_ORIGIN_FRAME, i, "signature"), where.getSignature()); - } - } + s.setTag(format(DD_CODE_ORIGIN_FRAME, 0, "file"), location.getFile()); + s.setTag(format(DD_CODE_ORIGIN_FRAME, 0, "method"), location.getMethod()); + s.setTag(format(DD_CODE_ORIGIN_FRAME, 0, "line"), location.getLines().get(0)); + s.setTag(format(DD_CODE_ORIGIN_FRAME, 0, "type"), location.getType()); + s.setTag(format(DD_CODE_ORIGIN_FRAME, 0, "signature"), signature); } } - return agentSpans; } public boolean entrySpanProbe() { return entrySpanProbe; } - /** look "back" to find exit spans that may have already come and gone */ - private AgentSpan findSpan(AgentSpan candidate) { - if (candidate == null) { - return null; - } - AgentSpan span = candidate; - AgentSpan localRootSpan = candidate.getLocalRootSpan(); - if (localRootSpan.getTag(getId()) != null) { - span = (AgentSpan) localRootSpan.getTag(getId()); - } - return span; - } - @Override - public void buildLocation(InstrumentationResult result) { + public void buildLocation(MethodInfo methodInfo) { String type = where.getTypeName(); String method = where.getMethodName(); List lines = null; String file = where.getSourceFile(); - if (result != null) { - type = result.getTypeName(); - method = result.getMethodName(); - if (result.getMethodStart() != -1) { - lines = singletonList(String.valueOf(result.getMethodStart())); + if (methodInfo != null) { + type = methodInfo.getTypeName(); + method = methodInfo.getMethodName(); + if (methodInfo.getMethodStart() != -1) { + lines = singletonList(String.valueOf(methodInfo.getMethodStart())); } if (file == null) { - file = result.getSourceFileName(); - } - if (entrySpanProbe) { - stackTraceElements = - singletonList(new StackTraceElement(type, method, file, result.getMethodStart())); + file = methodInfo.getSourceFileName(); } + signature = methodInfo.getSignature(); } this.location = new ProbeLocation(type, method, file, lines); } - - private List getUserStackFrames() { - return StackWalkerFactory.INSTANCE.walk( - stream -> - stream - .filter(element -> !DebuggerContext.isClassNameExcluded(element.getClassName())) - .limit(maxFrames) - .collect(Collectors.toList())); - } } diff --git a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/DebugSessionStatus.java b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/DebugSessionStatus.java index 3570f8de6e8..4497a1f8d58 100644 --- a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/DebugSessionStatus.java +++ b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/DebugSessionStatus.java @@ -1,11 +1,24 @@ package com.datadog.debugger.probe; public enum DebugSessionStatus { - NONE, + NONE { + @Override + public boolean isDefined() { + return false; + } + }, ACTIVE, DISABLED; + public boolean isDefined() { + return true; + } + public boolean isDisabled() { return this == DISABLED; } + + public boolean isActive() { + return this == ACTIVE; + } } diff --git a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/ExceptionProbe.java b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/ExceptionProbe.java index 247dd5ad7eb..61307e5059b 100644 --- a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/ExceptionProbe.java +++ b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/ExceptionProbe.java @@ -7,7 +7,7 @@ import com.datadog.debugger.el.ProbeCondition; import com.datadog.debugger.exception.ExceptionProbeManager; import com.datadog.debugger.exception.Fingerprinter; -import com.datadog.debugger.instrumentation.InstrumentationResult; +import com.datadog.debugger.instrumentation.MethodInfo; import com.datadog.debugger.sink.Snapshot; import datadog.trace.bootstrap.debugger.CapturedContext; import datadog.trace.bootstrap.debugger.MethodLocation; @@ -136,12 +136,12 @@ private void clearExceptionRefs(Snapshot snapshot) { } @Override - public void buildLocation(InstrumentationResult result) { + public void buildLocation(MethodInfo methodInfo) { String type = where.getTypeName(); String method = where.getMethodName(); - if (result != null) { - type = result.getTypeName(); - method = result.getMethodName(); + if (methodInfo != null) { + type = methodInfo.getTypeName(); + method = methodInfo.getMethodName(); } // drop line number for exception probe this.location = new ProbeLocation(type, method, where.getSourceFile(), emptyList()); diff --git a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/LogProbe.java b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/LogProbe.java index cc437a1cb17..9a8c0f59a0b 100644 --- a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/LogProbe.java +++ b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/LogProbe.java @@ -59,31 +59,6 @@ public class LogProbe extends ProbeDefinition implements Sampled { private static final Limits LIMITS = new Limits(1, 3, 8192, 5); private static final int LOG_MSG_LIMIT = 8192; - @SuppressForbidden // String#split(String) - private static Map getDebugSessions() { - HashMap sessions = new HashMap<>(); - TracerAPI tracer = AgentTracer.get(); - if (tracer != null) { - AgentSpan span = tracer.activeSpan(); - if (span instanceof DDSpan) { - DDSpanContext context = (DDSpanContext) span.context(); - String debug = context.getPropagationTags().getDebugPropagation(); - if (debug != null) { - String[] entries = debug.split(","); - for (String entry : entries) { - if (!entry.contains(":")) { - sessions.put("*", entry); - } else { - String[] values = entry.split(":"); - sessions.put(values[0], values[1]); - } - } - } - } - } - return sessions; - } - /** Stores part of a templated message either a str or an expression */ public static class Segment { private final String str; @@ -302,6 +277,7 @@ public String toString() { private final Capture capture; private final Sampling sampling; + private transient Consumer snapshotProcessor; // no-arg constructor is required by Moshi to avoid creating instance with unsafe and by-passing // constructors, including field initializers. @@ -367,6 +343,22 @@ private LogProbe( this.sampling = sampling; } + public LogProbe(LogProbe.Builder builder) { + this( + builder.language, + builder.probeId, + builder.tagStrs, + builder.where, + builder.evaluateAt, + builder.template, + builder.segments, + builder.captureSnapshot, + builder.probeCondition, + builder.capture, + builder.sampling); + this.snapshotProcessor = builder.snapshotProcessor; + } + @SuppressForbidden // String#split(String) private static List parseWatchesFromTags(Tag[] tags) { if (tags == null || tags.length == 0) { @@ -580,6 +572,9 @@ public void commit( DebuggerSink sink = DebuggerAgent.getSink(); if (shouldCommit) { commitSnapshot(snapshot, sink); + if (snapshotProcessor != null) { + snapshotProcessor.accept(snapshot); + } } else { sink.skipSnapshot(id, DebuggerContext.SkipCause.CONDITION); } @@ -760,8 +755,8 @@ public boolean shouldSend() { DebugSessionStatus status = getDebugSessionStatus(); // an ACTIVE status overrides the sampling as the sampling decision was made by the trigger // probe - return status == DebugSessionStatus.ACTIVE - || !status.isDisabled() && sampled && condition && !hasConditionErrors; + return status.isActive() + || !status.isDefined() && sampled && condition && !hasConditionErrors; } public boolean shouldReportError() { @@ -951,6 +946,12 @@ public static class Builder extends ProbeDefinition.Builder { private ProbeCondition probeCondition; private Capture capture; private Sampling sampling; + private Consumer snapshotProcessor; + + public Builder snapshotProcessor(Consumer processor) { + this.snapshotProcessor = processor; + return this; + } public Builder template(String template, List segments) { this.template = template; @@ -988,18 +989,32 @@ public Builder sampling(double rateLimit) { } public LogProbe build() { - return new LogProbe( - language, - probeId, - tagStrs, - where, - evaluateAt, - template, - segments, - captureSnapshot, - probeCondition, - capture, - sampling); + return new LogProbe(this); + } + } + + @SuppressForbidden // String#split(String) + private static Map getDebugSessions() { + HashMap sessions = new HashMap<>(); + TracerAPI tracer = AgentTracer.get(); + if (tracer != null) { + AgentSpan span = tracer.activeSpan(); + if (span instanceof DDSpan) { + DDSpanContext context = (DDSpanContext) span.context(); + String debug = context.getPropagationTags().getDebugPropagation(); + if (debug != null) { + String[] entries = debug.split(","); + for (String entry : entries) { + if (!entry.contains(":")) { + sessions.put("*", entry); + } else { + String[] values = entry.split(":"); + sessions.put(values[0], values[1]); + } + } + } + } } + return sessions; } } diff --git a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/ProbeDefinition.java b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/ProbeDefinition.java index c4f9f0ae045..f493634a43c 100644 --- a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/ProbeDefinition.java +++ b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/ProbeDefinition.java @@ -107,12 +107,12 @@ public MethodLocation getEvaluateAt() { return evaluateAt; } - public void buildLocation(InstrumentationResult result) { + public void buildLocation(MethodInfo methodInfo) { String type = where.getTypeName(); String method = where.getMethodName(); - if (result != null) { - type = result.getTypeName(); - method = result.getMethodName(); + if (methodInfo != null) { + type = methodInfo.getTypeName(); + method = methodInfo.getMethodName(); } List lines = where.getLines() != null ? Arrays.asList(where.getLines()) : null; this.location = new ProbeLocation(type, method, where.getSourceFile(), lines); diff --git a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/CapturedSnapshotTest.java b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/CapturedSnapshotTest.java index ce3ea578b0f..b41472b9aad 100644 --- a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/CapturedSnapshotTest.java +++ b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/CapturedSnapshotTest.java @@ -264,7 +264,6 @@ public void veryOldClassFile() throws Exception { public void oldClass1_1() throws Exception { final String CLASS_NAME = "org.apache.commons.lang.BooleanUtils"; // compiled with jdk 1.1 TestSnapshotListener listener = installSingleProbe(CLASS_NAME, "toBoolean", null); - when(config.isDebuggerVerifyByteCode()).thenReturn(true); Class testClass = loadClass( CLASS_NAME, getClass().getResource("/classfiles/BooleanUtils.classfile").getFile()); @@ -1103,7 +1102,7 @@ public void globalRateLimitSnapshot() throws IOException, URISyntaxException { Configuration config = Configuration.builder() .setService(SERVICE_NAME) - .addLogProbes(Arrays.asList(probe1, probe2)) + .add(probe1, probe2) .add(new LogProbe.Sampling(1)) .build(); TestSnapshotListener listener = installProbes(config); diff --git a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/CapturingTestBase.java b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/CapturingTestBase.java index 1a1b3fd77b5..0261865624c 100644 --- a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/CapturingTestBase.java +++ b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/CapturingTestBase.java @@ -3,18 +3,14 @@ import static com.datadog.debugger.util.MoshiSnapshotHelper.NOT_CAPTURED_REASON; import static com.datadog.debugger.util.MoshiSnapshotTestHelper.VALUE_ADAPTER; import static com.datadog.debugger.util.TestHelper.setFieldInConfig; -import static java.util.Arrays.asList; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; import com.datadog.debugger.instrumentation.InstrumentationResult; import com.datadog.debugger.probe.LogProbe; import com.datadog.debugger.probe.ProbeDefinition; -import com.datadog.debugger.probe.SpanDecorationProbe; -import com.datadog.debugger.probe.TriggerProbe; import com.datadog.debugger.sink.DebuggerSink; import com.datadog.debugger.sink.ProbeStatusSink; import com.datadog.debugger.util.MoshiHelper; @@ -325,12 +321,9 @@ protected static LogProbe createProbe( return createProbeBuilder(id, typeName, methodName, signature, lines).build(); } - protected TestSnapshotListener installProbes(LogProbe... logProbes) { + protected TestSnapshotListener installProbes(ProbeDefinition... probes) { return installProbes( - Configuration.builder() - .setService(CapturedSnapshotTest.SERVICE_NAME) - .addLogProbes(asList(logProbes)) - .build() /*, logProbes*/); + Configuration.builder().setService(CapturedSnapshotTest.SERVICE_NAME).add(probes).build()); } public static LogProbe.Builder createProbeBuilder( @@ -349,10 +342,8 @@ public static LogProbe.Builder createProbeBuilder( .sampling(new LogProbe.Sampling(100)); } - protected TestSnapshotListener installProbes( - Configuration configuration, ProbeDefinition... probes) { - - config = mockConfig(); + protected TestSnapshotListener installProbes(Configuration configuration) { + config = getConfig(); instrumentationListener = new MockInstrumentationListener(); probeStatusSink = mock(ProbeStatusSink.class); @@ -372,22 +363,14 @@ protected TestSnapshotListener installProbes( DebuggerAgentHelper.injectSink(listener); DebuggerContext.initProbeResolver( - (encodedId) -> - resolver( - encodedId, - configuration.getLogProbes(), - configuration.getSpanDecorationProbes(), - configuration.getTriggerProbes())); + (encodedId) -> resolver(encodedId, configuration.getDefinitions())); DebuggerContext.initClassFilter(new DenyListHelper(null)); DebuggerContext.initValueSerializer(new JsonSnapshotSerializer()); - Collection logProbes = configuration.getLogProbes(); - if (logProbes != null) { - for (LogProbe probe : logProbes) { - if (probe.getSampling() != null) { - ProbeRateLimiter.setRate( - probe.getId(), probe.getSampling().getEventsPerSecond(), probe.isCaptureSnapshot()); - } + for (LogProbe probe : configuration.getLogProbes()) { + if (probe.getSampling() != null) { + ProbeRateLimiter.setRate( + probe.getId(), probe.getSampling().getEventsPerSecond(), probe.isCaptureSnapshot()); } } if (configuration.getSampling() != null) { @@ -397,47 +380,24 @@ protected TestSnapshotListener installProbes( return listener; } - public static Config mockConfig() { - Config config = mock(Config.class); - when(config.isDebuggerEnabled()).thenReturn(true); - when(config.isDebuggerClassFileDumpEnabled()).thenReturn(true); - when(config.isDebuggerVerifyByteCode()).thenReturn(false); - when(config.getFinalDebuggerSnapshotUrl()) - .thenReturn("http://localhost:8126/debugger/v1/input"); - when(config.getFinalDebuggerSymDBUrl()).thenReturn("http://localhost:8126/symdb/v1/input"); - when(config.getDebuggerCodeOriginMaxUserFrames()).thenReturn(20); + public static Config getConfig() { + Config config = Config.get(); + setFieldInConfig(config, "debuggerEnabled", true); + setFieldInConfig(config, "debuggerClassFileDumpEnabled", true); + setFieldInConfig(config, "debuggerVerifyByteCode", false); + setFieldInConfig(config, "debuggerCodeOriginMaxUserFrames", 20); return config; } - public static ProbeImplementation resolver( - String encodedId, - Collection logProbes, - Collection spanDecorationProbes, - Collection triggerProbes) { - if (logProbes != null) { - for (LogProbe probe : logProbes) { - if (probe.getProbeId().getEncodedId().equals(encodedId)) { - return probe; - } - } - } - if (spanDecorationProbes != null) { - for (SpanDecorationProbe probe : spanDecorationProbes) { - if (probe.getProbeId().getEncodedId().equals(encodedId)) { - return probe; - } - } - } - if (triggerProbes != null) { - for (TriggerProbe probe : triggerProbes) { - if (probe.getProbeId().getEncodedId().equals(encodedId)) { - return probe; - } + public ProbeImplementation resolver(String encodedId, List probes) { + for (ProbeDefinition probe : probes) { + if (probe.getProbeId().getEncodedId().equals(encodedId)) { + return probe; } } - return null; + return configurationUpdater.resolve(encodedId); } protected TestSnapshotListener installSingleProbeAtExit( diff --git a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/ConfigurationComparerTest.java b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/ConfigurationComparerTest.java index 8e670c635e0..2ad30096120 100644 --- a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/ConfigurationComparerTest.java +++ b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/ConfigurationComparerTest.java @@ -222,7 +222,7 @@ public void hasProbeRelatedChangesWhenDenyListAddedWithProbe() { instrumentationResults.put( probe.getId(), new InstrumentationResult( - InstrumentationResult.Status.INSTALLED, null, "com.datadog.Blocked", "method")); + InstrumentationResult.Status.INSTALLED, "com.datadog.Blocked", "method")); Configuration noFilterConfig = createConfig(Collections.singletonList(probe)); Configuration config = Configuration.builder() @@ -446,7 +446,7 @@ private void doAllLoadedChangedClasses( } } - private static Configuration createConfig(List logProbes) { - return new Configuration(SERVICE_NAME, logProbes); + private static Configuration createConfig(List probes) { + return new Configuration(SERVICE_NAME, probes); } } diff --git a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/ConfigurationTest.java b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/ConfigurationTest.java index b27c0f64d04..2bfb3e5afad 100644 --- a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/ConfigurationTest.java +++ b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/ConfigurationTest.java @@ -299,11 +299,7 @@ private Configuration createConfig1() { LogProbe.Sampling globalSampling = new LogProbe.Sampling(10.0); return new Configuration( "service1", - asList(metric1), - asList(probe1, log1), - asList(span1), - asList(triggerProbe), - asList(spanDecoration1), + asList(metric1, probe1, log1, span1, triggerProbe, spanDecoration1), allowList, denyList, globalSampling); @@ -347,11 +343,7 @@ private Configuration createConfig2() { LogProbe.Sampling globalSampling = new LogProbe.Sampling(10.0); return new Configuration( "service2", - asList(metric2), - asList(probe2, log2), - asList(span2), - asList(triggerProbe), - asList(spanDecoration2), + asList(metric2, probe2, log2, span2, triggerProbe, spanDecoration2), allowList, denyList, globalSampling); diff --git a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/DebuggerTransformerTest.java b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/DebuggerTransformerTest.java index e8780cdd149..71d116ddd70 100644 --- a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/DebuggerTransformerTest.java +++ b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/DebuggerTransformerTest.java @@ -37,6 +37,8 @@ import java.io.InputStreamReader; import java.lang.instrument.Instrumentation; import java.lang.reflect.Field; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -137,9 +139,8 @@ void setup() { @Test public void testDump() { Config config = createConfig(); - final String JAVA_IO_TMPDIR = "java.io.tmpdir"; - String initialTmpDir = System.getProperty(JAVA_IO_TMPDIR); - System.setProperty(JAVA_IO_TMPDIR, "/tmp"); + Path initialTmpDir = DebuggerTransformer.DUMP_PATH; + DebuggerTransformer.DUMP_PATH = Paths.get("/tmp/debugger"); try { when(config.isDebuggerClassFileDumpEnabled()).thenReturn(true); File instrumentedClassFile = new File("/tmp/debugger/java.util.ArrayList.class"); @@ -166,7 +167,7 @@ public void testDump() { Assertions.assertTrue(instrumentedClassFile.delete()); Assertions.assertTrue(origClassFile.delete()); } finally { - System.setProperty(JAVA_IO_TMPDIR, initialTmpDir); + DebuggerTransformer.DUMP_PATH = initialTmpDir; } } @@ -311,8 +312,7 @@ public void classGenerationFailed() { Configuration configuration = Configuration.builder() .setService(SERVICE_NAME) - .addSpanProbes(Collections.singletonList(mockProbe)) - .addLogProbes(Arrays.asList(logProbe1, logProbe2)) + .add(mockProbe, logProbe1, logProbe2) .build(); AtomicReference lastResult = new AtomicReference<>(null); ProbeStatusSink probeStatusSink = mock(ProbeStatusSink.class); diff --git a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/LogProbesInstrumentationTest.java b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/LogProbesInstrumentationTest.java index eb5f2070b6d..4bad3cdba68 100644 --- a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/LogProbesInstrumentationTest.java +++ b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/LogProbesInstrumentationTest.java @@ -25,7 +25,6 @@ import java.lang.instrument.ClassFileTransformer; import java.lang.instrument.Instrumentation; import java.net.URISyntaxException; -import java.util.Arrays; import java.util.Collection; import java.util.List; import net.bytebuddy.agent.ByteBuddyAgent; @@ -504,11 +503,7 @@ private TestSnapshotListener installSingleProbe( } private TestSnapshotListener installProbes(LogProbe... logProbes) { - return installProbes( - Configuration.builder() - .setService(SERVICE_NAME) - .addLogProbes(Arrays.asList(logProbes)) - .build()); + return installProbes(Configuration.builder().setService(SERVICE_NAME).add(logProbes).build()); } private static LogProbe.Builder createProbeBuilder( diff --git a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/MetricProbesInstrumentationTest.java b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/MetricProbesInstrumentationTest.java index 2bdc0bb8f23..27cb2b1e7f2 100644 --- a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/MetricProbesInstrumentationTest.java +++ b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/MetricProbesInstrumentationTest.java @@ -35,7 +35,6 @@ import java.lang.instrument.Instrumentation; import java.net.URISyntaxException; import java.util.ArrayList; -import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -1313,10 +1312,7 @@ private MetricForwarderListener installMetricProbes(Configuration configuration) private MetricForwarderListener installMetricProbes(MetricProbe... metricProbes) { return installMetricProbes( - Configuration.builder() - .setService(SERVICE_NAME) - .addMetricProbes(Arrays.asList(metricProbes)) - .build()); + Configuration.builder().setService(SERVICE_NAME).add(metricProbes).build()); } static class MetricForwarderListener implements DebuggerContext.MetricForwarder { diff --git a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/SpanDecorationProbeInstrumentationTest.java b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/SpanDecorationProbeInstrumentationTest.java index fc73adb04eb..09f0a4054eb 100644 --- a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/SpanDecorationProbeInstrumentationTest.java +++ b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/SpanDecorationProbeInstrumentationTest.java @@ -712,11 +712,7 @@ private static SpanDecorationProbe.Builder createProbeBuilder( private void installSpanProbes(String expectedClassName, SpanDecorationProbe... probes) { installSpanDecorationProbes( - expectedClassName, - Configuration.builder() - .setService(SERVICE_NAME) - .addSpanDecorationProbes(asList(probes)) - .build()); + expectedClassName, Configuration.builder().setService(SERVICE_NAME).add(probes).build()); } private void installSpanDecorationProbes(String expectedClassName, Configuration configuration) { diff --git a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/SpanProbeInstrumentationTest.java b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/SpanProbeInstrumentationTest.java index 151d0d66514..957b5d47177 100644 --- a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/SpanProbeInstrumentationTest.java +++ b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/SpanProbeInstrumentationTest.java @@ -19,7 +19,6 @@ import java.io.IOException; import java.net.URISyntaxException; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import org.joor.Reflect; import org.junit.jupiter.api.Test; @@ -134,10 +133,7 @@ private MockTracer installSingleSpan( private MockTracer installSpanProbes(SpanProbe... spanProbes) { return installSpanProbes( - Configuration.builder() - .setService(SERVICE_NAME) - .addSpanProbes(Arrays.asList(spanProbes)) - .build()); + Configuration.builder().setService(SERVICE_NAME).add(spanProbes).build()); } private MockTracer installSpanProbes(Configuration configuration) { diff --git a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/TransformerDefinitionMatcherTest.java b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/TransformerDefinitionMatcherTest.java index 131d70f0bef..25d976feebd 100644 --- a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/TransformerDefinitionMatcherTest.java +++ b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/TransformerDefinitionMatcherTest.java @@ -1,16 +1,13 @@ package com.datadog.debugger.agent; import static com.datadog.debugger.util.ClassFileHelperTest.getClassFileBytes; -import static java.util.Collections.emptyList; +import static java.util.Arrays.asList; import static org.junit.jupiter.api.Assertions.*; import com.datadog.debugger.probe.LogProbe; import com.datadog.debugger.probe.MetricProbe; import com.datadog.debugger.probe.ProbeDefinition; -import com.datadog.debugger.probe.SpanProbe; import datadog.trace.bootstrap.debugger.ProbeId; -import java.util.Arrays; -import java.util.Collection; import java.util.List; import org.junit.jupiter.api.Test; @@ -22,15 +19,14 @@ public class TransformerDefinitionMatcherTest { @Test public void empty() { - TransformerDefinitionMatcher matcher = createMatcher(emptyList(), emptyList(), emptyList()); + TransformerDefinitionMatcher matcher = createMatcher(); assertTrue(matcher.isEmpty()); } @Test public void fullQualifiedClassName() { LogProbe probe = createProbe(PROBE_ID1, "java.lang.String", "indexOf"); - TransformerDefinitionMatcher matcher = - createMatcher(emptyList(), Arrays.asList(probe), emptyList()); + TransformerDefinitionMatcher matcher = createMatcher(probe); List probeDefinitions = match(matcher, String.class); assertEquals(1, probeDefinitions.size()); assertEquals(PROBE_ID1, probeDefinitions.get(0).getProbeId()); @@ -39,8 +35,7 @@ public void fullQualifiedClassName() { @Test public void simpleClassName() { LogProbe probe = createProbe(PROBE_ID1, "String", "indexOf"); - TransformerDefinitionMatcher matcher = - createMatcher(emptyList(), Arrays.asList(probe), emptyList()); + TransformerDefinitionMatcher matcher = createMatcher(probe); List probeDefinitions = match(matcher, String.class); assertEquals(1, probeDefinitions.size()); assertEquals(PROBE_ID1, probeDefinitions.get(0).getProbeId()); @@ -49,8 +44,7 @@ public void simpleClassName() { @Test public void simpleClassNameNoClassRedefined() { LogProbe probe = createProbe(PROBE_ID1, "String", "indexOf"); - TransformerDefinitionMatcher matcher = - createMatcher(emptyList(), Arrays.asList(probe), emptyList()); + TransformerDefinitionMatcher matcher = createMatcher(probe); List probeDefinitions = matcher.match( null, @@ -64,8 +58,7 @@ public void simpleClassNameNoClassRedefined() { @Test public void sourceFileFullFileName() { LogProbe probe = createProbe(PROBE_ID1, "src/main/java/java/lang/String.java", 23); - TransformerDefinitionMatcher matcher = - createMatcher(emptyList(), Arrays.asList(probe), emptyList()); + TransformerDefinitionMatcher matcher = createMatcher(probe); List probeDefinitions = match(matcher, String.class); assertEquals(1, probeDefinitions.size()); assertEquals(PROBE_ID1, probeDefinitions.get(0).getProbeId()); @@ -75,8 +68,7 @@ public void sourceFileFullFileName() { public void sourceFileAbsoluteFileName() { LogProbe probe = createProbe(PROBE_ID1, "/home/user/project/src/main/java/java/lang/String.java", 23); - TransformerDefinitionMatcher matcher = - createMatcher(emptyList(), Arrays.asList(probe), emptyList()); + TransformerDefinitionMatcher matcher = createMatcher(probe); List probeDefinitions = match(matcher, String.class); assertEquals(1, probeDefinitions.size()); assertEquals(PROBE_ID1, probeDefinitions.get(0).getProbeId()); @@ -85,8 +77,7 @@ public void sourceFileAbsoluteFileName() { @Test public void sourceFileSimpleFileName() { LogProbe probe = createProbe(PROBE_ID1, "String.java", 23); - TransformerDefinitionMatcher matcher = - createMatcher(emptyList(), Arrays.asList(probe), emptyList()); + TransformerDefinitionMatcher matcher = createMatcher(probe); List probeDefinitions = match(matcher, String.class); assertEquals(1, probeDefinitions.size()); assertEquals(PROBE_ID1, probeDefinitions.get(0).getProbeId()); @@ -96,8 +87,7 @@ public void sourceFileSimpleFileName() { public void multiProbesFQN() { LogProbe probe1 = createProbe(PROBE_ID1, "java.lang.String", "indexOf"); LogProbe probe2 = createProbe(PROBE_ID2, "java.lang.String", "substring"); - TransformerDefinitionMatcher matcher = - createMatcher(emptyList(), Arrays.asList(probe1, probe2), emptyList()); + TransformerDefinitionMatcher matcher = createMatcher(probe1, probe2); List probeDefinitions = match(matcher, String.class); assertEquals(2, probeDefinitions.size()); assertEquals(PROBE_ID1, probeDefinitions.get(0).getProbeId()); @@ -108,8 +98,7 @@ public void multiProbesFQN() { public void multiProbesSimpleName() { LogProbe probe1 = createProbe(PROBE_ID1, "String", "indexOf"); LogProbe probe2 = createProbe(PROBE_ID2, "String", "substring"); - TransformerDefinitionMatcher matcher = - createMatcher(emptyList(), Arrays.asList(probe1, probe2), emptyList()); + TransformerDefinitionMatcher matcher = createMatcher(probe1, probe2); List probeDefinitions = match(matcher, String.class); assertEquals(2, probeDefinitions.size()); assertEquals(PROBE_ID1, probeDefinitions.get(0).getProbeId()); @@ -120,8 +109,7 @@ public void multiProbesSimpleName() { public void multiProbesSourceFile() { LogProbe probe1 = createProbe(PROBE_ID1, "src/main/java/java/lang/String.java", 23); LogProbe probe2 = createProbe(PROBE_ID2, "src/main/java/java/lang/String.java", 42); - TransformerDefinitionMatcher matcher = - createMatcher(emptyList(), Arrays.asList(probe1, probe2), emptyList()); + TransformerDefinitionMatcher matcher = createMatcher(probe1, probe2); List probeDefinitions = match(matcher, String.class); assertEquals(2, probeDefinitions.size()); assertEquals(PROBE_ID1, probeDefinitions.get(0).getProbeId()); @@ -132,8 +120,7 @@ public void multiProbesSourceFile() { public void mixedProbesFQNSimple() { LogProbe probe1 = createProbe(PROBE_ID1, "java.lang.String", "indexOf"); LogProbe probe2 = createProbe(PROBE_ID2, "String", "substring"); - TransformerDefinitionMatcher matcher = - createMatcher(emptyList(), Arrays.asList(probe1, probe2), emptyList()); + TransformerDefinitionMatcher matcher = createMatcher(probe1, probe2); List probeDefinitions = match(matcher, String.class); assertEquals(2, probeDefinitions.size()); assertEquals(PROBE_ID1, probeDefinitions.get(0).getProbeId()); @@ -144,8 +131,7 @@ public void mixedProbesFQNSimple() { public void mixedSnapshotMetricProbes() { LogProbe probe1 = createProbe(PROBE_ID1, "java.lang.String", "indexOf"); MetricProbe probe2 = createMetric(PROBE_ID2, "String", "substring"); - TransformerDefinitionMatcher matcher = - createMatcher(Arrays.asList(probe2), Arrays.asList(probe1), emptyList()); + TransformerDefinitionMatcher matcher = createMatcher(probe1, probe2); List probeDefinitions = match(matcher, String.class); assertEquals(2, probeDefinitions.size()); assertEquals(PROBE_ID1, probeDefinitions.get(0).getProbeId()); @@ -155,8 +141,7 @@ public void mixedSnapshotMetricProbes() { @Test public void partialSimpleNameShouldNotMatch() { LogProbe probe1 = createProbe(PROBE_ID1, "SuperString.java", 11); - TransformerDefinitionMatcher matcher = - createMatcher(emptyList(), Arrays.asList(probe1), emptyList()); + TransformerDefinitionMatcher matcher = createMatcher(probe1); List probeDefinitions = match(matcher, String.class); assertEquals(0, probeDefinitions.size()); } @@ -166,8 +151,7 @@ public void mixedSourceFileName() { LogProbe probe1 = createProbe(PROBE_ID1, "src/main/java/java/lang/String.java", 23); LogProbe probe2 = createProbe(PROBE_ID2, "myproject/src/main/java/java/lang/String.java", 42); LogProbe probe3 = createProbe(PROBE_ID3, "String.java", 11); - TransformerDefinitionMatcher matcher = - createMatcher(emptyList(), Arrays.asList(probe1, probe2, probe3), emptyList()); + TransformerDefinitionMatcher matcher = createMatcher(probe1, probe2, probe3); List probeDefinitions = match(matcher, String.class); assertEquals(3, probeDefinitions.size()); assertEquals(PROBE_ID1.getId(), probeDefinitions.get(0).getId()); @@ -175,12 +159,8 @@ public void mixedSourceFileName() { assertEquals(PROBE_ID3.getId(), probeDefinitions.get(2).getId()); } - private TransformerDefinitionMatcher createMatcher( - Collection metricProbes, - Collection logProbes, - Collection spanProbes) { - return new TransformerDefinitionMatcher( - new Configuration(SERVICE_NAME, metricProbes, logProbes, spanProbes)); + private TransformerDefinitionMatcher createMatcher(ProbeDefinition... probes) { + return new TransformerDefinitionMatcher(new Configuration(SERVICE_NAME, asList(probes))); } private static List match(TransformerDefinitionMatcher matcher, Class clazz) { diff --git a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/origin/CodeOriginTest.java b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/origin/CodeOriginTest.java index 574bdcccd1e..12db52ff5b6 100644 --- a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/origin/CodeOriginTest.java +++ b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/origin/CodeOriginTest.java @@ -4,8 +4,8 @@ import static datadog.trace.api.DDTags.DD_CODE_ORIGIN_FRAME; import static datadog.trace.api.DDTags.DD_CODE_ORIGIN_PREFIX; import static datadog.trace.api.DDTags.DD_CODE_ORIGIN_TYPE; +import static datadog.trace.util.AgentThreadFactory.AgentThread.TASK_SCHEDULER; import static java.lang.String.format; -import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; import static java.util.Arrays.asList; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -18,7 +18,7 @@ import com.datadog.debugger.agent.CapturingTestBase; import com.datadog.debugger.codeorigin.DefaultCodeOriginRecorder; import com.datadog.debugger.probe.CodeOriginProbe; -import com.datadog.debugger.probe.LogProbe; +import com.datadog.debugger.probe.ProbeDefinition; import com.datadog.debugger.probe.Where; import com.datadog.debugger.util.ClassNameFiltering; import com.datadog.debugger.util.TestSnapshotListener; @@ -29,15 +29,12 @@ import datadog.trace.api.interceptor.MutableSpan; import datadog.trace.bootstrap.debugger.DebuggerContext; import datadog.trace.bootstrap.debugger.DebuggerContext.ClassNameFilter; -import datadog.trace.bootstrap.debugger.Limits; import datadog.trace.bootstrap.debugger.ProbeId; import datadog.trace.core.CoreTracer; -import java.io.File; +import datadog.trace.core.DDSpan; +import datadog.trace.util.AgentTaskScheduler; import java.io.IOException; import java.net.URISyntaxException; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Optional; @@ -46,7 +43,6 @@ import java.util.stream.Collectors; import org.jetbrains.annotations.NotNull; import org.joor.Reflect; -import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -63,6 +59,8 @@ public class CodeOriginTest extends CapturingTestBase { private DefaultCodeOriginRecorder codeOriginRecorder; + private TestSnapshotListener listener; + private TestTraceInterceptor traceInterceptor; public static ClassNameFilter classNameFilter = @@ -93,88 +91,59 @@ public void before() { setFieldInConfig(InstrumenterConfig.get(), "codeOriginEnabled", true); } - @AfterAll - public static void collectClasses() throws IOException { - File[] files = - Paths.get(System.getProperty("java.io.tmpdir"), "debugger/com/datadog/debugger") - .toFile() - .listFiles( - file -> { - String name = file.getName(); - return name.startsWith("CodeOrigin") && name.endsWith(".class"); - }); - - if (new File("build").exists()) { - File buildDir = new File("build/debugger"); - buildDir.mkdirs(); - for (File file : files) { - Files.copy( - file.toPath(), Paths.get(buildDir.getAbsolutePath(), file.getName()), REPLACE_EXISTING); - } - } - } - @Test public void basicInstrumentation() throws Exception { final String className = "com.datadog.debugger.CodeOrigin01"; installProbes(codeOriginProbes(className)); final Class testClass = compileAndLoadClass(className); - checkResults(testClass, "fullTrace", false); + checkResults(testClass, "fullTrace", 0); } @Test public void withDebug1() throws Exception { final String className = "com.datadog.debugger.CodeOrigin02"; - installProbes(codeOriginProbes(className)); + installProbes(); final Class testClass = compileAndLoadClass(className); - checkResults(testClass, "debug_1", true); + codeOriginRecorder.captureCodeOrigin(testClass.getMethod("entry"), true); + codeOriginRecorder.captureCodeOrigin(testClass.getMethod("exit"), false); + checkResults(testClass, "fullTrace", 0); + checkResults(testClass, "debug_1", 2); } @Test - public void withLogProbe() throws IOException, URISyntaxException { + public void withLogProbe() throws Exception { final String CLASS_NAME = "com.datadog.debugger.CodeOrigin03"; - LogProbe logProbe = - createProbeBuilder(PROBE_ID, CLASS_NAME, "entry", "()") - .capture(1, 100, 255, Limits.DEFAULT_FIELD_COUNT) - .build(); - List probes = codeOriginProbes(CLASS_NAME); - probes.add(logProbe); - installProbes(probes); + installProbes( + createProbeBuilder(PROBE_ID, CLASS_NAME, "entry", "()").captureSnapshot(true).build()); final Class testClass = compileAndLoadClass(CLASS_NAME); - checkResults(testClass, "debug_1", true); + codeOriginRecorder.captureCodeOrigin(testClass.getMethod("entry"), true); + codeOriginRecorder.captureCodeOrigin(testClass.getMethod("exit"), false); + checkResults(testClass, "debug_1", 3); } @Test public void doubleEntry() throws IOException, URISyntaxException { final String className = "com.datadog.debugger.CodeOrigin05"; - List probes = - asList( - new CodeOriginProbe( - CODE_ORIGIN_ID1, true, Where.of(className, "entry", "()", "53"), MAX_FRAMES), - new CodeOriginProbe( - CODE_ORIGIN_ID2, false, Where.of(className, "exit", "()", "62"), MAX_FRAMES), - new CodeOriginProbe( - CODE_ORIGIN_DOUBLE_ENTRY_ID, - true, - Where.of(className, "doubleEntry", "()", "66"), - MAX_FRAMES)); - installProbes(probes); + installProbes( + new CodeOriginProbe(CODE_ORIGIN_ID1, true, Where.of(className, "entry", "()", "53")), + new CodeOriginProbe(CODE_ORIGIN_ID2, false, Where.of(className, "exit", "()", "62")), + new CodeOriginProbe( + CODE_ORIGIN_DOUBLE_ENTRY_ID, true, Where.of(className, "doubleEntry", "()", "66"))); final Class testClass = compileAndLoadClass(className); - checkResults(testClass, "fullTrace", false); + checkResults(testClass, "fullTrace", 0); List trace = traceInterceptor.getTrace(); MutableSpan span = trace.get(0); // this should be entry but until we get the ordering resolved, it's this. - assertEquals("doubleEntry", span.getTag(format(DD_CODE_ORIGIN_FRAME, 0, "method"))); + assertEquals("entry", span.getTag(format(DD_CODE_ORIGIN_FRAME, 0, "method"))); } @Test public void stackDepth() throws IOException, URISyntaxException { final String CLASS_NAME = "com.datadog.debugger.CodeOrigin04"; installProbes( - new CodeOriginProbe( - CODE_ORIGIN_ID1, true, Where.of(CLASS_NAME, "exit", "()", "39"), MAX_FRAMES)); + new CodeOriginProbe(CODE_ORIGIN_ID1, true, Where.of(CLASS_NAME, "exit", "()", "39"))); Class testClass = compileAndLoadClass("com.datadog.debugger.CodeOrigin04"); countFrames(testClass, 10); @@ -237,55 +206,64 @@ public void testDuplicateInstrumentations() } @NotNull - private List codeOriginProbes(String type) { + private CodeOriginProbe[] codeOriginProbes(String type) { CodeOriginProbe entry = - new CodeOriginProbe(CODE_ORIGIN_ID1, true, Where.of(type, "entry", "()", "53"), MAX_FRAMES); + new CodeOriginProbe(CODE_ORIGIN_ID1, true, Where.of(type, "entry", "()", "53")); CodeOriginProbe exit = - new CodeOriginProbe(CODE_ORIGIN_ID2, false, Where.of(type, "exit", "()", "60"), MAX_FRAMES); - return new ArrayList<>(asList(entry, exit)); + new CodeOriginProbe(CODE_ORIGIN_ID2, false, Where.of(type, "exit", "()", "60")); + return new CodeOriginProbe[] {entry, exit}; } - private void checkResults(Class testClass, String parameter, boolean includeSnapshot) { + private void checkResults(Class testClass, String parameter, int snapshotsExpected) { int result = Reflect.onClass(testClass).call("main", parameter).get(); assertEquals(0, result); - List spans = traceInterceptor.getTrace(); + List spans = (List) traceInterceptor.getTrace(); assertEquals(3, spans.size()); assertEquals("main", spans.get(2).getLocalRootSpan().getOperationName()); - List list = + List list = spans.stream() .filter(span -> !span.getOperationName().equals("exit")) .collect(Collectors.toList()); - for (MutableSpan span : list) { - checkEntrySpanTags(span, includeSnapshot); + for (DDSpan span : list) { + checkEntrySpanTags(span, snapshotsExpected != 0); } - Optional exit = + + assertEquals( + snapshotsExpected, + listener.snapshots.stream() + .filter(s -> s.getCaptures().getEntry() != null && s.getCaptures().getReturn() != null) + .collect(Collectors.toList()) + .size()); + + Optional exit = spans.stream().filter(span -> span.getOperationName().equals("exit")).findFirst(); assertTrue(exit.isPresent()); - exit.ifPresent(span -> checkExitSpanTags(span, includeSnapshot)); + exit.ifPresent(span -> checkExitSpanTags(span, false)); } @Override - protected TestSnapshotListener installProbes(LogProbe... probes) { - TestSnapshotListener listener = super.installProbes(probes); + protected TestSnapshotListener installProbes(ProbeDefinition... probes) { + listener = super.installProbes(probes); DebuggerContext.initClassNameFilter(classNameFilter); - codeOriginRecorder = new DefaultCodeOriginRecorder(config, configurationUpdater); + codeOriginRecorder = + new DefaultCodeOriginRecorder( + config, + configurationUpdater, + new AgentTaskScheduler(TASK_SCHEDULER) { + @Override + public void execute(Runnable target) { + target.run(); + } + }); DebuggerContext.initCodeOrigin(codeOriginRecorder); return listener; } - protected TestSnapshotListener installProbes(List probes) { - return installProbes(probes.toArray(new LogProbe[0])); - } - - private static void checkEntrySpanTags(MutableSpan span, boolean includeSnapshot) { - String keys = format("Existing keys for %s: %s", span.getOperationName(), ldKeys(span)); - - assertEquals(span.getTag(DD_CODE_ORIGIN_TYPE), "entry", keys); - assertKeyPresent(span, DD_CODE_ORIGIN_TYPE); + private static void checkEntrySpanTags(DDSpan span, boolean includeSnapshot) { assertKeyPresent(span, format(DD_CODE_ORIGIN_FRAME, 0, "file")); assertKeyPresent(span, format(DD_CODE_ORIGIN_FRAME, 0, "line")); assertNotEquals(-1, span.getTag(format(DD_CODE_ORIGIN_FRAME, 0, "line"))); @@ -300,9 +278,12 @@ private static void checkEntrySpanTags(MutableSpan span, boolean includeSnapshot } } - private static void assertKeyPresent(MutableSpan span, String key) { + private static void assertKeyPresent(DDSpan span, String key) { assertNotNull( - span.getTag(key), format("'%s' key missing in '%s' span.", key, span.getOperationName())); + span.getTag(key), + format( + "'%s' key missing in '%s' span. current keys: %s", + key, span.getOperationName(), ldKeys(span))); } private static void assertKeyNotPresent(MutableSpan span, String key) { @@ -311,7 +292,7 @@ private static void assertKeyNotPresent(MutableSpan span, String key) { format("'%s' key found in '%s' span when it shouldn't be.", key, span.getOperationName())); } - private static void checkExitSpanTags(MutableSpan span, boolean includeSnapshot) { + private static void checkExitSpanTags(DDSpan span, boolean includeSnapshot) { String keys = format("Existing keys for %s: %s", span.getOperationName(), new TreeSet<>(ldKeys(span))); diff --git a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/probe/LogProbeTest.java b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/probe/LogProbeTest.java index 77847c79aed..778eefb8abb 100644 --- a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/probe/LogProbeTest.java +++ b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/probe/LogProbeTest.java @@ -1,6 +1,6 @@ package com.datadog.debugger.probe; -import static com.datadog.debugger.agent.CapturingTestBase.mockConfig; +import static com.datadog.debugger.agent.CapturingTestBase.getConfig; import static com.datadog.debugger.util.LogProbeTestHelper.parseTemplate; import static java.lang.String.format; import static java.lang.Thread.currentThread; @@ -28,7 +28,6 @@ import datadog.trace.bootstrap.instrumentation.api.ScopeSource; import datadog.trace.bootstrap.instrumentation.api.Tags; import datadog.trace.core.CoreTracer; -import java.util.UUID; import java.util.stream.Stream; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -80,7 +79,7 @@ public void noDebugSession() { } private boolean fillSnapshot(DebugSessionStatus status) { - DebuggerAgentHelper.injectSink(new DebuggerSink(mockConfig(), mock(ProbeStatusSink.class))); + DebuggerAgentHelper.injectSink(new DebuggerSink(getConfig(), mock(ProbeStatusSink.class))); TracerAPI tracer = CoreTracer.builder().idGenerationStrategy(IdGenerationStrategy.fromName("random")).build(); AgentTracer.registerIfAbsent(tracer); @@ -92,8 +91,7 @@ private boolean fillSnapshot(DebugSessionStatus status) { span.setTag(Tags.PROPAGATED_DEBUG, DEBUG_SESSION_ID + ":0"); } - Builder builder = - createLog("I'm in a debug session").probeId(UUID.randomUUID().toString(), 0); + Builder builder = createLog("I'm in a debug session").probeId(ProbeId.newId()); if (status != DebugSessionStatus.NONE) { builder.tags(format("session_id:%s", DEBUG_SESSION_ID)); } diff --git a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/trigger/TriggerProbeTest.java b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/trigger/TriggerProbeTest.java index 93d18843464..8b6eb5f556a 100644 --- a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/trigger/TriggerProbeTest.java +++ b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/trigger/TriggerProbeTest.java @@ -3,7 +3,6 @@ import static com.datadog.debugger.el.DSL.*; import static com.datadog.debugger.util.TestHelper.setFieldInConfig; import static java.lang.String.format; -import static java.util.Collections.singletonList; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static utils.InstrumentationTestHelper.compileAndLoadClass; @@ -69,11 +68,7 @@ public void cooldown() throws IOException, URISyntaxException { "()", null, new Sampling(10, 10.0)); - installProbes( - Configuration.builder() - .setService(SERVICE_NAME) - .addTriggerProbes(singletonList(probe1)) - .build()); + installProbes(Configuration.builder().setService(SERVICE_NAME).add(probe1).build()); Class testClass = compileAndLoadClass(className); int runs = 10000; for (int i = 0; i < runs; i++) { @@ -125,11 +120,7 @@ public void sampling() throws IOException, URISyntaxException { "()", null, new Sampling(10.0)); - Configuration config = - Configuration.builder() - .setService(SERVICE_NAME) - .addTriggerProbes(singletonList(probe1)) - .build(); + Configuration config = Configuration.builder().setService(SERVICE_NAME).add(probe1).build(); installProbes(config); Class testClass = compileAndLoadClass(className); for (int i = 0; i < 100; i++) { @@ -155,11 +146,7 @@ public void conditions() throws IOException, URISyntaxException { "(int)", new ProbeCondition(when(lt(ref("value"), value(25))), "value < 25"), new Sampling(10.0)); - installProbes( - Configuration.builder() - .setService(SERVICE_NAME) - .addTriggerProbes(singletonList(probe1)) - .build()); + installProbes(Configuration.builder().setService(SERVICE_NAME).add(probe1).build()); Class testClass = compileAndLoadClass(className); for (int i = 0; i < 100; i++) { Reflect.onClass(testClass).call("main", i).get(); diff --git a/dd-java-agent/agent-debugger/src/test/resources/com/datadog/debugger/CodeOrigin02.java b/dd-java-agent/agent-debugger/src/test/resources/com/datadog/debugger/CodeOrigin02.java index 88c5e75ae2f..b15127496a2 100644 --- a/dd-java-agent/agent-debugger/src/test/resources/com/datadog/debugger/CodeOrigin02.java +++ b/dd-java-agent/agent-debugger/src/test/resources/com/datadog/debugger/CodeOrigin02.java @@ -56,7 +56,7 @@ public static void entry() throws NoSuchMethodException { } } - private static void exit() { + public static void exit() { int x = 47 / 3; } diff --git a/dd-java-agent/agent-debugger/src/test/resources/com/datadog/debugger/CodeOrigin03.java b/dd-java-agent/agent-debugger/src/test/resources/com/datadog/debugger/CodeOrigin03.java index 27da370e2c4..cfd0b9ee219 100644 --- a/dd-java-agent/agent-debugger/src/test/resources/com/datadog/debugger/CodeOrigin03.java +++ b/dd-java-agent/agent-debugger/src/test/resources/com/datadog/debugger/CodeOrigin03.java @@ -56,7 +56,7 @@ public static void entry() throws NoSuchMethodException { } } - private static void exit() { + public static void exit() { int x = 47 / 3; } diff --git a/dd-smoke-tests/debugger-integration-tests/src/test/java/datadog/smoketest/BaseIntegrationTest.java b/dd-smoke-tests/debugger-integration-tests/src/test/java/datadog/smoketest/BaseIntegrationTest.java index 9d402367efc..d366c671f48 100644 --- a/dd-smoke-tests/debugger-integration-tests/src/test/java/datadog/smoketest/BaseIntegrationTest.java +++ b/dd-smoke-tests/debugger-integration-tests/src/test/java/datadog/smoketest/BaseIntegrationTest.java @@ -466,27 +466,18 @@ protected Configuration createConfig(LogProbe logProbe) { } protected Configuration createMetricConfig(MetricProbe metricProbe) { - return Configuration.builder() - .setService(getAppId()) - .addMetricProbes(Collections.singletonList(metricProbe)) - .build(); + return Configuration.builder().setService(getAppId()).add(metricProbe).build(); } protected Configuration createSpanDecoConfig(SpanDecorationProbe spanDecorationProbe) { - return Configuration.builder() - .setService(getAppId()) - .addSpanDecorationProbes(Collections.singletonList(spanDecorationProbe)) - .build(); + return Configuration.builder().setService(getAppId()).add(spanDecorationProbe).build(); } protected Configuration createSpanConfig(SpanProbe spanProbe) { - return Configuration.builder() - .setService(getAppId()) - .addSpanProbes(Collections.singletonList(spanProbe)) - .build(); + return Configuration.builder().setService(getAppId()).add(spanProbe).build(); } - protected Configuration createConfig(Collection logProbes) { + protected Configuration createConfig(List logProbes) { return new Configuration(getAppId(), logProbes); } @@ -496,7 +487,7 @@ protected Configuration createConfig( Configuration.FilterList denyList) { return Configuration.builder() .setService(getAppId()) - .addLogProbes(logProbes) + .add(logProbes) .addAllowList(allowList) .addDenyList(denyList) .build();