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 89ca5262ad4..c0e6cc6ec4c 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 @@ -67,8 +67,8 @@ public interface ExceptionDebugger { void handleException(Throwable t, AgentSpan span); } - public interface SpanDebugger { - String captureSnapshot(String signature); + public interface CodeOriginRecorder { + String captureCodeOrigin(String signature); } private static volatile ProbeResolver probeResolver; @@ -77,7 +77,7 @@ public interface SpanDebugger { private static volatile Tracer tracer; private static volatile ValueSerializer valueSerializer; private static volatile ExceptionDebugger exceptionDebugger; - private static volatile SpanDebugger spanDebugger; + private static volatile CodeOriginRecorder codeOriginRecorder; public static void initProbeResolver(ProbeResolver probeResolver) { DebuggerContext.probeResolver = probeResolver; @@ -103,8 +103,8 @@ public static void initExceptionDebugger(ExceptionDebugger exceptionDebugger) { DebuggerContext.exceptionDebugger = exceptionDebugger; } - public static void initSpanDebugger(SpanDebugger spanDebugger) { - DebuggerContext.spanDebugger = spanDebugger; + public static void initCodeOrigin(CodeOriginRecorder codeOriginRecorder) { + DebuggerContext.codeOriginRecorder = codeOriginRecorder; } /** @@ -342,14 +342,14 @@ public static void commit( } } - public static String captureSnapshot(String signature) { + public static String captureCodeOrigin(String signature) { try { - SpanDebugger debugger = spanDebugger; - if (debugger != null) { - return debugger.captureSnapshot(signature); + CodeOriginRecorder recorder = codeOriginRecorder; + if (recorder != null) { + return recorder.captureCodeOrigin(signature); } } catch (Exception ex) { - LOGGER.debug("Error in addSnapshot: ", ex); + LOGGER.debug("Error in captureCodeOrigin: ", ex); } return null; } diff --git a/dd-java-agent/agent-debugger/debugger-bootstrap/src/main/java/datadog/trace/bootstrap/debugger/spanorigin/SpanOriginInfo.java b/dd-java-agent/agent-debugger/debugger-bootstrap/src/main/java/datadog/trace/bootstrap/debugger/spanorigin/CodeOriginInfo.java similarity index 51% rename from dd-java-agent/agent-debugger/debugger-bootstrap/src/main/java/datadog/trace/bootstrap/debugger/spanorigin/SpanOriginInfo.java rename to dd-java-agent/agent-debugger/debugger-bootstrap/src/main/java/datadog/trace/bootstrap/debugger/spanorigin/CodeOriginInfo.java index a37490e959f..ce4ba1324c2 100644 --- a/dd-java-agent/agent-debugger/debugger-bootstrap/src/main/java/datadog/trace/bootstrap/debugger/spanorigin/SpanOriginInfo.java +++ b/dd-java-agent/agent-debugger/debugger-bootstrap/src/main/java/datadog/trace/bootstrap/debugger/spanorigin/CodeOriginInfo.java @@ -1,27 +1,26 @@ package datadog.trace.bootstrap.debugger.spanorigin; -import static datadog.trace.bootstrap.debugger.DebuggerContext.captureSnapshot; +import static datadog.trace.bootstrap.debugger.DebuggerContext.captureCodeOrigin; import static java.util.Arrays.stream; import datadog.trace.api.InstrumenterConfig; -import datadog.trace.bootstrap.instrumentation.api.AgentSpan; import java.lang.reflect.Method; import java.util.stream.Collectors; -public class SpanOriginInfo { - public static void entry(AgentSpan span, Method method) { - if (InstrumenterConfig.get().isSpanOriginEnabled()) { +public class CodeOriginInfo { + public static void entry(Method method) { + if (InstrumenterConfig.get().isCodeOriginEnabled()) { String signature = stream(method.getParameterTypes()) .map(Class::getName) .collect(Collectors.joining(",", "(", ")")); - captureSnapshot(signature); + captureCodeOrigin(signature); } } - public static void exit(AgentSpan span) { - if (InstrumenterConfig.get().isSpanOriginEnabled()) { - span.getLocalRootSpan().setMetaStruct(captureSnapshot(null), span); + public static void exit() { + if (InstrumenterConfig.get().isCodeOriginEnabled()) { + captureCodeOrigin(null); } } } 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 a2fda5f56aa..24205cdb0d8 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,5 +1,6 @@ package com.datadog.debugger.agent; +import com.datadog.debugger.probe.DebuggerProbe; import com.datadog.debugger.probe.ExceptionProbe; import com.datadog.debugger.probe.LogProbe; import com.datadog.debugger.probe.MetricProbe; @@ -65,6 +66,7 @@ public int hashCode() { private final Collection metricProbes; private final Collection logProbes; private final Collection spanProbes; + private final Collection debuggerProbes; private final Collection spanDecorationProbes; private final FilterList allowList; private final FilterList denyList; @@ -79,7 +81,7 @@ public Configuration( Collection metricProbes, Collection logProbes, Collection spanProbes) { - this(serviceName, metricProbes, logProbes, spanProbes, null, null, null, null); + this(serviceName, metricProbes, logProbes, spanProbes, null, null, null, null, null); } public Configuration( @@ -87,6 +89,7 @@ public Configuration( Collection metricProbes, Collection logProbes, Collection spanProbes, + Collection debuggerProbes, Collection spanDecorationProbes, FilterList allowList, FilterList denyList, @@ -95,6 +98,7 @@ public Configuration( this.metricProbes = metricProbes; this.logProbes = logProbes; this.spanProbes = spanProbes; + this.debuggerProbes = debuggerProbes; this.spanDecorationProbes = spanDecorationProbes; this.allowList = allowList; this.denyList = denyList; @@ -117,6 +121,10 @@ public Collection getSpanProbes() { return spanProbes; } + public Collection getDebuggerProbes() { + return debuggerProbes; + } + public Collection getSpanDecorationProbes() { return spanDecorationProbes; } @@ -135,6 +143,9 @@ public LogProbe.Sampling getSampling() { public Collection getDefinitions() { Collection result = new ArrayList<>(); + if (debuggerProbes != null) { + result.addAll(debuggerProbes); + } if (metricProbes != null) { result.addAll(metricProbes); } @@ -198,6 +209,7 @@ public static class Builder { private List metricProbes = null; private List logProbes = null; private List spanProbes = null; + private List debuggerProbes = null; private List spanDecorationProbes = null; private FilterList allowList = null; private FilterList denyList = null; @@ -214,6 +226,7 @@ public Configuration.Builder add(Collection definitio } for (ProbeDefinition definition : definitions) { if (definition instanceof MetricProbe) add((MetricProbe) definition); + if (definition instanceof DebuggerProbe) add((DebuggerProbe) definition); if (definition instanceof LogProbe) add((LogProbe) definition); if (definition instanceof SpanProbe) add((SpanProbe) definition); if (definition instanceof SpanDecorationProbe) add((SpanDecorationProbe) definition); @@ -245,6 +258,14 @@ public Configuration.Builder add(SpanProbe probe) { return this; } + public Configuration.Builder add(DebuggerProbe probe) { + if (debuggerProbes == null) { + debuggerProbes = new ArrayList<>(); + } + debuggerProbes.add(probe); + return this; + } + public Configuration.Builder add(SpanDecorationProbe probe) { if (spanDecorationProbes == null) { spanDecorationProbes = new ArrayList<>(); @@ -300,6 +321,16 @@ public Configuration.Builder addSpanProbes(Collection probes) { return this; } + public Configuration.Builder addDebuggerProbes(Collection probes) { + if (probes == null) { + return this; + } + for (DebuggerProbe probe : probes) { + add(probe); + } + return this; + } + public Configuration.Builder addSpanDecorationProbes(Collection probes) { if (probes == null) { return this; @@ -346,6 +377,7 @@ public Configuration.Builder add(Configuration other) { addMetricProbes(other.getMetricProbes()); addLogProbes(other.getLogProbes()); addSpanProbes(other.getSpanProbes()); + addDebuggerProbes(other.getDebuggerProbes()); addSpanDecorationProbes(other.getSpanDecorationProbes()); addAllowList(other.getAllowList()); addDenyList(other.getDenyList()); @@ -359,6 +391,7 @@ public Configuration build() { metricProbes, logProbes, spanProbes, + debuggerProbes, spanDecorationProbes, allowList, denyList, diff --git a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/DebuggerAgent.java b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/DebuggerAgent.java index 394448b8763..b0dfced64b9 100644 --- a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/DebuggerAgent.java +++ b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/DebuggerAgent.java @@ -3,13 +3,14 @@ import static com.datadog.debugger.agent.ConfigurationAcceptor.Source.REMOTE_CONFIG; import static datadog.trace.util.AgentThreadFactory.AGENT_THREAD_GROUP; +import com.datadog.debugger.codeorigin.CodeOriginProbeManager; +import com.datadog.debugger.codeorigin.DefaultCodeOriginRecorder; import com.datadog.debugger.exception.DefaultExceptionDebugger; import com.datadog.debugger.exception.ExceptionProbeManager; import com.datadog.debugger.sink.DebuggerSink; import com.datadog.debugger.sink.ProbeStatusSink; import com.datadog.debugger.sink.SnapshotSink; import com.datadog.debugger.sink.SymbolSink; -import com.datadog.debugger.snapshot.DefaultSpanDebugger; import com.datadog.debugger.symbol.SymDBEnablement; import com.datadog.debugger.symbol.SymbolAggregator; import com.datadog.debugger.uploader.BatchUploader; @@ -102,9 +103,10 @@ public static synchronized void run( config.getDebuggerMaxExceptionPerSecond()); DebuggerContext.initExceptionDebugger(defaultExceptionDebugger); } - if (config.isDebuggerSpanDebugEnabled()) { - DebuggerContext.initSpanDebugger( - new DefaultSpanDebugger(configurationUpdater, classNameFiltering)); + if (config.isDebuggerCodeOriginEnabled()) { + DebuggerContext.initCodeOrigin( + new DefaultCodeOriginRecorder( + new CodeOriginProbeManager(configurationUpdater, classNameFiltering))); } if (config.isDebuggerInstrumentTheWorld()) { setupInstrumentTheWorldTransformer( diff --git a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/DebuggerProductChangesListener.java b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/DebuggerProductChangesListener.java index c3341eae209..32791d34d5d 100644 --- a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/DebuggerProductChangesListener.java +++ b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/DebuggerProductChangesListener.java @@ -2,6 +2,7 @@ import static com.datadog.debugger.agent.ConfigurationAcceptor.Source.REMOTE_CONFIG; +import com.datadog.debugger.probe.DebuggerProbe; import com.datadog.debugger.probe.LogProbe; import com.datadog.debugger.probe.MetricProbe; import com.datadog.debugger.probe.ProbeDefinition; @@ -28,6 +29,7 @@ import org.slf4j.LoggerFactory; public class DebuggerProductChangesListener implements ProductListener { + public static final int MAX_ALLOWED_DEBUGGER_PROBES = 100; public static final int MAX_ALLOWED_METRIC_PROBES = 100; public static final int MAX_ALLOWED_LOG_PROBES = 100; public static final int MAX_ALLOWED_SPAN_PROBES = 100; @@ -35,6 +37,7 @@ public class DebuggerProductChangesListener implements ProductListener { public static final String LOG_PROBE_PREFIX = "logProbe_"; public static final String METRIC_PROBE_PREFIX = "metricProbe_"; public static final String SPAN_PROBE_PREFIX = "spanProbe_"; + public static final String DEBUGGER_PROBE_PREFIX = "debuggerProbe_"; public static final String SPAN_DECORATION_PROBE_PREFIX = "spanDecorationProbe_"; private static final Logger LOGGER = LoggerFactory.getLogger(DebuggerProductChangesListener.class); @@ -52,6 +55,9 @@ static class Adapter { static final JsonAdapter SPAN_PROBE_JSON_ADAPTER = MoshiHelper.createMoshiConfig().adapter(SpanProbe.class); + static final JsonAdapter DEBUGGER_PROBE_JSON_ADAPTER = + MoshiHelper.createMoshiConfig().adapter(DebuggerProbe.class); + static final JsonAdapter SPAN_DECORATION_PROBE_JSON_ADAPTER = MoshiHelper.createMoshiConfig().adapter(SpanDecorationProbe.class); @@ -71,6 +77,10 @@ static SpanProbe deserializeSpanProbe(byte[] content) throws IOException { return deserialize(SPAN_PROBE_JSON_ADAPTER, content); } + static DebuggerProbe deserializeDebuggerProbe(byte[] content) throws IOException { + return deserialize(DEBUGGER_PROBE_JSON_ADAPTER, content); + } + static SpanDecorationProbe deserializeSpanDecorationProbe(byte[] content) throws IOException { return deserialize(SPAN_DECORATION_PROBE_JSON_ADAPTER, content); } @@ -108,6 +118,9 @@ public void accept(ConfigKey configKey, byte[] content, PollingRateHinter pollin } else if (configId.startsWith(SPAN_PROBE_PREFIX)) { SpanProbe spanProbe = Adapter.deserializeSpanProbe(content); configChunks.put(configId, definitions -> definitions.add(spanProbe)); + } else if (configId.startsWith(DEBUGGER_PROBE_PREFIX)) { + DebuggerProbe debuggerProbe = Adapter.deserializeDebuggerProbe(content); + configChunks.put(configId, definitions -> definitions.add(debuggerProbe)); } else if (configId.startsWith(SPAN_DECORATION_PROBE_PREFIX)) { SpanDecorationProbe spanDecorationProbe = Adapter.deserializeSpanDecorationProbe(content); configChunks.put(configId, definitions -> definitions.add(spanDecorationProbe)); @@ -148,6 +161,7 @@ public void commit(PollingRateHinter pollingRateHinter) { static class DefinitionBuilder { private final Collection definitions = new ArrayList<>(); + private int debuggerProbeCount = 0; private int metricProbeCount = 0; private int logProbeCount = 0; private int spanProbeCount = 0; @@ -180,6 +194,15 @@ void add(SpanProbe probe) { spanProbeCount++; } + void add(DebuggerProbe probe) { + if (debuggerProbeCount >= MAX_ALLOWED_DEBUGGER_PROBES) { + LOGGER.debug("Max allowed debugger probes reached, ignoring new probe: {}", probe); + return; + } + definitions.add(probe); + debuggerProbeCount++; + } + void add(SpanDecorationProbe probe) { if (spanDecorationProbeCount >= MAX_ALLOWED_SPAN_DECORATION_PROBES) { LOGGER.debug("Max allowed span decoration probes reached, ignoring new probe: {}", probe); @@ -197,6 +220,8 @@ void addAll(Collection newDefinitions) { add((LogProbe) definition); } else if (definition instanceof SpanProbe) { add((SpanProbe) definition); + } else if (definition instanceof DebuggerProbe) { + add((DebuggerProbe) definition); } else if (definition instanceof SpanDecorationProbe) { add((SpanDecorationProbe) definition); } 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 61607cb6e0c..efd802f6f2f 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 @@ -6,6 +6,7 @@ import com.datadog.debugger.instrumentation.DiagnosticMessage; import com.datadog.debugger.instrumentation.InstrumentationResult; import com.datadog.debugger.instrumentation.MethodInfo; +import com.datadog.debugger.probe.DebuggerProbe; import com.datadog.debugger.probe.ExceptionProbe; import com.datadog.debugger.probe.ForceMethodInstrumentation; import com.datadog.debugger.probe.LogProbe; @@ -77,7 +78,12 @@ public class DebuggerTransformer implements ClassFileTransformer { private static final String CANNOT_FIND_LINE = "No executable code was found at %s:L%s"; private static final Pattern COMMA_PATTERN = Pattern.compile(","); private static final List> PROBE_ORDER = - Arrays.asList(MetricProbe.class, LogProbe.class, SpanDecorationProbe.class, SpanProbe.class); + Arrays.asList( + DebuggerProbe.class, + MetricProbe.class, + LogProbe.class, + SpanDecorationProbe.class, + SpanProbe.class); private static final String JAVA_IO_TMPDIR = "java.io.tmpdir"; private final Config config; @@ -212,7 +218,7 @@ private Map> mergeLocations( Map> mergedProbes = new HashMap<>(); for (ProbeDefinition definition : definitions) { Where where = definition.getWhere(); - if (definition instanceof ExceptionProbe) { + if (definition instanceof ForceMethodInstrumentation) { // normalize where for line => to precise method location where = Where.convertLineToMethod(definition.getWhere(), classFileLines); } @@ -411,7 +417,7 @@ private void verifyByteCode(String classFilePath, byte[] classFile) { if (!result.isEmpty()) { log.warn("Verification of instrumented class {} failed", classFilePath); log.debug("Verify result: {}", stringWriter); - throw new RuntimeException("Generated bydecode is invalid for " + classFilePath); + throw new RuntimeException("Generated bytecode is invalid for " + classFilePath); } } diff --git a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/codeorigin/CodeOriginProbeManager.java b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/codeorigin/CodeOriginProbeManager.java new file mode 100644 index 00000000000..cb221eebd8e --- /dev/null +++ b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/codeorigin/CodeOriginProbeManager.java @@ -0,0 +1,137 @@ +package com.datadog.debugger.codeorigin; + +import static com.datadog.debugger.agent.ConfigurationAcceptor.Source.SPAN_DEBUG; + +import com.datadog.debugger.agent.ConfigurationUpdater; +import com.datadog.debugger.exception.Fingerprinter; +import com.datadog.debugger.probe.CodeOriginProbe; +import com.datadog.debugger.probe.Where; +import com.datadog.debugger.util.ClassNameFiltering; +import datadog.trace.bootstrap.debugger.CapturedContext; +import datadog.trace.bootstrap.debugger.ProbeId; +import datadog.trace.bootstrap.instrumentation.api.AgentTracer; +import datadog.trace.util.AgentTaskScheduler; +import datadog.trace.util.stacktrace.StackWalkerFactory; +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.stream.Collectors; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class CodeOriginProbeManager { + private static final Logger LOG = LoggerFactory.getLogger(CodeOriginProbeManager.class); + + private Map fingerprints = new HashMap<>(); + + private final Map probes = new ConcurrentHashMap<>(); + + private final ClassNameFiltering classNameFiltering; + + private AgentTaskScheduler taskScheduler = AgentTaskScheduler.INSTANCE; + + private final ConfigurationUpdater configurationUpdater; + + public CodeOriginProbeManager( + ConfigurationUpdater configurationUpdater, ClassNameFiltering classNameFiltering) { + this.configurationUpdater = configurationUpdater; + this.classNameFiltering = classNameFiltering; + } + + public Collection getProbes() { + return probes.values(); + } + + void addFingerprint(String fingerprint, CodeOriginProbe probe) { + fingerprints.putIfAbsent(fingerprint, probe); + } + + String getProbeId(String fingerprint) { + return fingerprints.get(fingerprint).getProbeId().getId(); + } + + public ClassNameFiltering getClassNameFiltering() { + return classNameFiltering; + } + + public String createProbeForFrame(String signature) { + StackTraceElement element = findPlaceInStack(); + String fingerprint = Fingerprinter.fingerprint(element); + if (fingerprint == null) { + LOG.debug("Unable to fingerprint snapshot"); + return null; + } + CodeOriginProbe probe; + + if (!isAlreadyInstrumented(fingerprint)) { + Where where = + Where.convertLineToMethod( + element.getClassName(), + element.getMethodName(), + signature, + String.valueOf(element.getLineNumber())); + + probe = + new CodeOriginProbe( + new ProbeId(UUID.randomUUID().toString(), 0), where.getSignature(), where, this); + addFingerprint(fingerprint, probe); + + installProbe(probe); + if (AgentTracer.get().activeSpan() != null) { + probe.commit( + CapturedContext.EMPTY_CONTEXT, CapturedContext.EMPTY_CONTEXT, Collections.emptyList()); + } + + } else { + probe = fingerprints.get(fingerprint); + } + + return probe.getId(); + } + + public String installProbe(CodeOriginProbe probe) { + CodeOriginProbe installed = probes.putIfAbsent(probe.getId(), probe); + if (installed == null) { + taskScheduler.execute( + () -> { + configurationUpdater.accept(SPAN_DEBUG, getProbes()); + }); + return probe.getId(); + } + return installed.getId(); + } + + public ClassNameFiltering classNameFiltering() { + return classNameFiltering; + } + + public boolean isAlreadyInstrumented(String fingerprint) { + return fingerprints.containsKey(fingerprint); + } + + public AgentTaskScheduler taskScheduler() { + return taskScheduler; + } + + public CodeOriginProbeManager taskScheduler(AgentTaskScheduler taskScheduler) { + this.taskScheduler = taskScheduler; + return this; + } + + private StackTraceElement findPlaceInStack() { + return StackWalkerFactory.INSTANCE.walk( + stream -> { + List list = stream.collect(Collectors.toList()); + list = + list.stream() + .filter(element -> !classNameFiltering.isExcluded(element.getClassName())) + .collect(Collectors.toList()); + + return list.stream().findFirst().orElse(null); + }); + } +} 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 new file mode 100644 index 00000000000..8be9c8e3c75 --- /dev/null +++ b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/codeorigin/DebuggerConfiguration.java @@ -0,0 +1,16 @@ +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 new file mode 100644 index 00000000000..7c126cbdfee --- /dev/null +++ b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/codeorigin/DefaultCodeOriginRecorder.java @@ -0,0 +1,20 @@ +package com.datadog.debugger.codeorigin; + +import datadog.trace.bootstrap.debugger.DebuggerContext.CodeOriginRecorder; + +public class DefaultCodeOriginRecorder implements CodeOriginRecorder { + private final CodeOriginProbeManager probeManager; + + public DefaultCodeOriginRecorder(CodeOriginProbeManager probeManager) { + this.probeManager = probeManager; + } + + public CodeOriginProbeManager probeManager() { + return probeManager; + } + + @Override + public String captureCodeOrigin(String signature) { + return probeManager.createProbeForFrame(signature); + } +} 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 new file mode 100644 index 00000000000..47d26e2ab35 --- /dev/null +++ b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/CodeOriginProbe.java @@ -0,0 +1,175 @@ +package com.datadog.debugger.probe; + +import static com.datadog.debugger.codeorigin.DebuggerConfiguration.isDebuggerEnabled; +import static datadog.trace.api.DDTags.DD_CODE_ORIGIN_SNAPSHOT_ID; +import static datadog.trace.api.DDTags.DD_STACK_CODE_ORIGIN_FRAME; +import static datadog.trace.api.DDTags.DD_STACK_CODE_ORIGIN_TYPE; +import static java.lang.String.format; + +import com.datadog.debugger.agent.DebuggerAgent; +import com.datadog.debugger.codeorigin.CodeOriginProbeManager; +import com.datadog.debugger.instrumentation.InstrumentationResult; +import com.datadog.debugger.sink.Snapshot; +import com.datadog.debugger.util.ClassNameFiltering; +import datadog.trace.api.DDTags; +import datadog.trace.bootstrap.debugger.CapturedContext; +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.LinkedHashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class CodeOriginProbe extends LogProbe implements ForceMethodInstrumentation { + private static final Logger LOGGER = LoggerFactory.getLogger(CodeOriginProbe.class); + + private final String signature; + + private final boolean entrySpanProbe; + + private final transient CodeOriginProbeManager probeManager; + + public CodeOriginProbe( + ProbeId probeId, String signature, Where where, CodeOriginProbeManager probeManager) { + super(LANGUAGE, probeId, null, where, MethodLocation.EXIT, null, null, true, null, null, null); + this.signature = signature; + this.entrySpanProbe = signature != null; + this.probeManager = probeManager; + } + + @Override + public boolean isLineProbe() { + // these are always method probes even if there is a line number + return false; + } + + public boolean isEntrySpanProbe() { + return entrySpanProbe; + } + + @Override + public void evaluate( + CapturedContext context, CapturedContext.Status status, MethodLocation methodLocation) { + if (!MethodLocation.isSame(methodLocation, getEvaluateAt())) { + return; + } + + super.evaluate(context, status, methodLocation); + } + + @Override + public void commit( + CapturedContext entryContext, + CapturedContext exitContext, + List caughtExceptions) { + + AgentSpan span = AgentTracer.activeSpan(); + + if (span == null) { + LOGGER.debug("Could not find the exit span for probeId {}", id); + return; + } + String snapshotId = null; + if (isDebuggerEnabled(span)) { + Snapshot snapshot = createSnapshot(); + if (fillSnapshot(entryContext, exitContext, caughtExceptions, snapshot)) { + snapshotId = snapshot.getId(); + LOGGER.debug( + "committing exception probe id={}, snapshot id={}, exception id={}", + id, + snapshotId, + snapshot.getExceptionId()); + + commitSnapshot(snapshot, DebuggerAgent.getSink()); + } + } + applySpanOriginTags(span, snapshotId); + } + + private void applySpanOriginTags(AgentSpan span, String snapshotId) { + List entries = getUserStackFrames(); + if (!entries.isEmpty()) { + Set spans = new LinkedHashSet<>(); + AgentSpan rootSpan = span.getLocalRootSpan() != null ? span.getLocalRootSpan() : span; + if (rootSpan != null) { + spans.add(rootSpan); + } + int stackIndex = countExistingStacks(span); + if (entrySpanProbe) { + spans.add(span); + StackTraceElement entry = entries.get(0); + for (AgentSpan s : spans) { + s.setTag(DDTags.DD_CODE_ORIGIN_FILE, toFileName(entry.getClassName())); + s.setTag(DDTags.DD_CODE_ORIGIN_METHOD, entry.getMethodName()); + s.setTag(DDTags.DD_CODE_ORIGIN_LINE, entry.getLineNumber()); + s.setTag(DDTags.DD_CODE_ORIGIN_TYPE, entry.getClassName()); + s.setTag(DDTags.DD_CODE_ORIGIN_METHOD_SIGNATURE, signature); + if (snapshotId != null) { + s.setTag(DD_CODE_ORIGIN_SNAPSHOT_ID, snapshotId); + } + } + } + for (AgentSpan s : spans) { + s.setTag(format(DD_STACK_CODE_ORIGIN_TYPE, stackIndex), entrySpanProbe ? "entry" : "exit"); + + for (int i = 0; i < entries.size(); i++) { + StackTraceElement info = entries.get(i); + s.setTag(format(DD_STACK_CODE_ORIGIN_FRAME, stackIndex, i, "file"), info.getFileName()); + s.setTag(format(DD_STACK_CODE_ORIGIN_FRAME, stackIndex, i, "line"), info.getLineNumber()); + s.setTag( + format(DD_STACK_CODE_ORIGIN_FRAME, stackIndex, i, "method"), info.getMethodName()); + s.setTag(format(DD_STACK_CODE_ORIGIN_FRAME, stackIndex, i, "type"), info.getClassName()); + if (i == 0 && snapshotId != null) { + s.setTag(format(DD_STACK_CODE_ORIGIN_FRAME, stackIndex, i, "snapshot_id"), snapshotId); + } + } + } + } + } + + private int countExistingStacks(AgentSpan span) { + Set keys = span.getLocalRootSpan().getTags().keySet(); + int count = 0; + while (keys.contains(format(DD_STACK_CODE_ORIGIN_FRAME, count, 0, "file"))) { + count++; + } + + return count; + } + + @Override + public void buildLocation(InstrumentationResult result) { + String type = where.getTypeName(); + String method = where.getMethodName(); + if (result != null) { + type = result.getTypeName(); + method = result.getMethodName(); + } + // drop line number for exception probe + this.location = new ProbeLocation(type, method, where.getSourceFile(), null); + } + + private List getUserStackFrames() { + ClassNameFiltering classNameFiltering = probeManager.getClassNameFiltering(); + + return StackWalkerFactory.INSTANCE.walk( + stream -> + stream + .filter(element -> !classNameFiltering.isExcluded(element.getClassName())) + .collect(Collectors.toList())); + } + + private static String toFileName(String className) { + // this has a number of issues including non-public top level classes and non-Java JVM + // languages it's here to fulfill the RFC requirement of a file name in the location. + // This part of the spec needs a little review to find consensus on how to handle this but + // until then this is here to fill that gap. + return className.replace('.', '/') + ".java"; + } +} diff --git a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/DebuggerProbe.java b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/DebuggerProbe.java new file mode 100644 index 00000000000..6caa49e0892 --- /dev/null +++ b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/DebuggerProbe.java @@ -0,0 +1,113 @@ +package com.datadog.debugger.probe; + +import com.datadog.debugger.agent.DebuggerAgent; +import com.datadog.debugger.agent.Generated; +import com.datadog.debugger.instrumentation.CapturedContextInstrumentor; +import com.datadog.debugger.instrumentation.DiagnosticMessage; +import com.datadog.debugger.instrumentation.InstrumentationResult; +import com.datadog.debugger.instrumentation.MethodInfo; +import datadog.trace.bootstrap.debugger.CapturedContext; +import datadog.trace.bootstrap.debugger.MethodLocation; +import datadog.trace.bootstrap.debugger.ProbeId; +import datadog.trace.bootstrap.instrumentation.api.AgentSpan; +import datadog.trace.bootstrap.instrumentation.api.AgentTracer; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class DebuggerProbe extends ProbeDefinition { + private static final Logger LOGGER = LoggerFactory.getLogger(DebuggerProbe.class); + + // no-arg constructor is required by Moshi to avoid creating instance with unsafe and by-passing + // constructors, including field initializers. + public DebuggerProbe() { + this(LANGUAGE, null, null); + } + + public DebuggerProbe(String language, ProbeId probeId, Where where) { + super(language, probeId, (Tag[]) null, where, MethodLocation.ENTRY); + } + + @Override + public InstrumentationResult.Status instrument( + MethodInfo methodInfo, List diagnostics, List probeIds) { + return new CapturedContextInstrumentor(this, methodInfo, diagnostics, probeIds, false, null) + .instrument(); + } + + @Override + public void evaluate( + CapturedContext context, CapturedContext.Status status, MethodLocation methodLocation) { + decorateTags(); + } + + private void decorateTags() { + AgentTracer.TracerAPI tracerAPI = AgentTracer.get(); + + AgentSpan agentSpan = tracerAPI.activeSpan().getLocalRootSpan(); + agentSpan.setTag("_dd.p.debug", "1"); + agentSpan.setTag("_dd.ld.probe_id", probeId.getId()); + + DebuggerAgent.getSink().getProbeStatusSink().addEmitting(probeId); + } + + @Override + public CapturedContext.Status createStatus() { + return new CapturedContext.Status(this); + } + + @Generated + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + DebuggerProbe that = (DebuggerProbe) o; + return Objects.equals(language, that.language) + && Objects.equals(id, that.id) + && version == that.version + && Arrays.equals(tags, that.tags) + && Objects.equals(tagMap, that.tagMap) + && Objects.equals(where, that.where) + && Objects.equals(evaluateAt, that.evaluateAt); + } + + @Generated + @Override + public int hashCode() { + int result = Objects.hash(language, id, version, where, evaluateAt); + result = 31 * result + Arrays.hashCode(tags); + return result; + } + + @Generated + @Override + public String toString() { + return getClass().getSimpleName() + + "{" + + "language='" + + language + + '\'' + + ", id='" + + id + + '\'' + + ", version=" + + version + + ", where=" + + where + + ", evaluateAt=" + + evaluateAt + + "} "; + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder extends ProbeDefinition.Builder { + public DebuggerProbe build() { + return new DebuggerProbe(language, probeId, where); + } + } +} diff --git a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/ForceMethodInstrumentation.java b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/ForceMethodInstrumentation.java index 6fc7ef540ac..43dcb2bc4f2 100644 --- a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/ForceMethodInstrumentation.java +++ b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/ForceMethodInstrumentation.java @@ -3,9 +3,9 @@ /** * This marker interface indicates that, regardless of the {@link Where} field, this probe should * always be treated as a method probe for purposes of instrumentation and evaluation. In the cases - * of {@link ExceptionProbe} and {@link SpanDebuggerProbe}, e.g., these probes need to be - * instrumented as method probes even though specific lines are listed in their {@link Where} fields - * because the information they are collecting is very much related to specific lines but the - * lifecycle of a line probe is insufficient to gather that data. + * of {@link ExceptionProbe} and {@link CodeOriginProbe}, e.g., these probes need to be instrumented + * as method probes even though specific lines are listed in their {@link Where} fields because the + * information they are collecting is very much related to specific lines but the lifecycle of a + * line probe is insufficient to gather that data. */ public interface ForceMethodInstrumentation {} diff --git a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/SpanDebuggerProbe.java b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/SpanDebuggerProbe.java deleted file mode 100644 index 73384d86c76..00000000000 --- a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/SpanDebuggerProbe.java +++ /dev/null @@ -1,197 +0,0 @@ -package com.datadog.debugger.probe; - -import static com.datadog.debugger.snapshot.SpanDebug.ALL_FRAMES; -import static com.datadog.debugger.snapshot.SpanDebug.CAPTURE_ALL_PROBES; -import static com.datadog.debugger.snapshot.SpanDebug.CAPTURE_ORIGIN_FRAMES; -import static com.datadog.debugger.snapshot.SpanDebug.ORIGIN_FRAME_ONLY; -import static com.datadog.debugger.snapshot.SpanDebug.isSpanDebugEnabled; -import static datadog.trace.api.DDTags.DD_EXIT_LOCATION_SNAPSHOT_ID; -import static java.lang.String.format; - -import com.datadog.debugger.agent.DebuggerAgent; -import com.datadog.debugger.instrumentation.InstrumentationResult; -import com.datadog.debugger.sink.Snapshot; -import com.datadog.debugger.snapshot.SpanDebuggerProbeManager; -import com.datadog.debugger.util.ClassNameFiltering; -import datadog.trace.api.DDTags; -import datadog.trace.bootstrap.debugger.CapturedContext; -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.core.DDSpanContext; -import datadog.trace.util.stacktrace.StackWalkerFactory; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.stream.Collectors; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class SpanDebuggerProbe extends LogProbe implements ForceMethodInstrumentation { - private static final Logger LOGGER = LoggerFactory.getLogger(SpanDebuggerProbe.class); - - private final String signature; - - private final boolean entrySpanProbe; - - private final transient SpanDebuggerProbeManager probeManager; - - public SpanDebuggerProbe( - ProbeId probeId, String signature, Where where, SpanDebuggerProbeManager probeManager) { - super(LANGUAGE, probeId, null, where, MethodLocation.EXIT, null, null, true, null, null, null); - this.signature = signature; - this.entrySpanProbe = signature != null; - this.probeManager = probeManager; - } - - @Override - public boolean isLineProbe() { - // these are always method probes even if there is a line number - return false; - } - - public boolean isEntrySpanProbe() { - return entrySpanProbe; - } - - @Override - public void evaluate( - CapturedContext context, CapturedContext.Status status, MethodLocation methodLocation) { - if (!MethodLocation.isSame(methodLocation, getEvaluateAt())) { - return; - } - - super.evaluate(context, status, methodLocation); - } - - @Override - public void commit( - CapturedContext entryContext, - CapturedContext exitContext, - List caughtExceptions) { - - AgentSpan span = findCorrectSpan(AgentTracer.activeSpan()); - - if (span == null) { - LOGGER.debug("Could not find the exit span for probeId {}", id); - return; - } - applySpanOriginTags(span); - commitSnapshot(span, entryContext, exitContext, caughtExceptions); - } - - private void commitSnapshot( - AgentSpan span, - CapturedContext entryContext, - CapturedContext exitContext, - List caughtExceptions) { - if (isSpanDebugEnabled(span, CAPTURE_ORIGIN_FRAMES, CAPTURE_ALL_PROBES)) { - String key = - entrySpanProbe ? DDTags.DD_ENTRY_LOCATION_SNAPSHOT_ID : DD_EXIT_LOCATION_SNAPSHOT_ID; - Snapshot snapshot = createSnapshot(); - if (fillSnapshot(entryContext, exitContext, caughtExceptions, snapshot)) { - LOGGER.debug( - "committing exception probe id={}, snapshot id={}, exception id={}", - id, - snapshot.getId(), - snapshot.getExceptionId()); - - span.setTag(key, snapshot.getId()); - if (entrySpanProbe && span.getLocalRootSpan() != null) { - span.getLocalRootSpan().setTag(key, snapshot.getId()); - } - commitSnapshot(snapshot, DebuggerAgent.getSink()); - } - } - } - - private AgentSpan findCorrectSpan(AgentSpan span) { - if (entrySpanProbe) { - return span; - } - if (span.getLocalRootSpan().context() instanceof DDSpanContext) { - DDSpanContext rootContext = (DDSpanContext) span.getLocalRootSpan().context(); - - Map rootMetaStruct = rootContext.getMetaStruct(); - Object object = rootMetaStruct.get(probeId.getId()); - rootContext.setMetaStruct(probeId.getId(), null); - return object instanceof AgentSpan ? (AgentSpan) object : null; - } - // just don't record the exit info if we can't find the correct span - return null; - } - - private void applySpanOriginTags(AgentSpan span) { - if (isSpanDebugEnabled(span, ORIGIN_FRAME_ONLY, ALL_FRAMES)) { - List entries = getUserStackFrames(); - if (!entries.isEmpty()) { - if (entrySpanProbe) { - StackTraceElement entry = entries.get(0); - Set spans = new LinkedHashSet<>(); - spans.add(span); - AgentSpan rootSpan = span.getLocalRootSpan(); - if (rootSpan != null && rootSpan.getTags().get(DDTags.DD_ENTRY_LOCATION_FILE) == null) { - spans.add(rootSpan); - } - for (AgentSpan s : spans) { - s.setTag("_dd.di.has_code_location", true); - s.setTag(DDTags.DD_ENTRY_LOCATION_FILE, toFileName(entry.getClassName())); - s.setTag(DDTags.DD_ENTRY_METHOD, entry.getMethodName()); - s.setTag(DDTags.DD_ENTRY_LINE, entry.getLineNumber()); - s.setTag(DDTags.DD_ENTRY_TYPE, entry.getClassName()); - s.setTag(DDTags.DD_ENTRY_METHOD_SIGNATURE, signature); - } - } else { - span.setTag("_dd.di.has_code_location", true); - if (isSpanDebugEnabled(span, ORIGIN_FRAME_ONLY)) { - entries = entries.subList(0, 1); - } - for (int i = 0; i < entries.size(); i++) { - StackTraceElement element = entries.get(i); - span.setTag( - format(DDTags.DD_EXIT_LOCATION_FILE, i), toFileName(element.getClassName())); - span.setTag(format(DDTags.DD_EXIT_LOCATION_METHOD, i), element.getMethodName()); - span.setTag(format(DDTags.DD_EXIT_LOCATION_LINE, i), element.getLineNumber()); - span.setTag(format(DDTags.DD_EXIT_LOCATION_TYPE, i), element.getClassName()); - } - } - } - } - } - - @Override - public void buildLocation(InstrumentationResult result) { - String type = where.getTypeName(); - String method = where.getMethodName(); - if (result != null) { - type = result.getTypeName(); - method = result.getMethodName(); - } - // drop line number for exception probe - this.location = new ProbeLocation(type, method, where.getSourceFile(), null); - } - - private List getUserStackFrames() { - ClassNameFiltering classNameFiltering = probeManager.getClassNameFiltering(); - List entries = - StackWalkerFactory.INSTANCE.walk( - stream -> - stream - .filter(element -> !classNameFiltering.isExcluded(element.getClassName())) - .collect(Collectors.toList())); - - return entries; - } - - private static String toFileName(String className) { - // this has a number of issues including non-public top level classes and non-Java JVM - // languages it's here to fulfill the RFC requirement of a file name in the location. - // This part of the spec needs a little review to find consensus on how to handle this but - // until then this is here to fill that gap. - return className.replace('.', '/') + ".java"; - } -} diff --git a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/Where.java b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/Where.java index d18259c16fb..f4685559e76 100644 --- a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/Where.java +++ b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/Where.java @@ -47,10 +47,6 @@ public static Where convertLineToMethod( return new Where(typeName, methodName, signature, lines, null); } - public static Where convertLineToMethod(String sourceFile, int line) { - return new Where(null, null, null, new SourceLine[] {new SourceLine(line)}, sourceFile); - } - protected static SourceLine[] sourceLines(String[] defs) { if (defs == null) { return null; diff --git a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/snapshot/DefaultSpanDebugger.java b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/snapshot/DefaultSpanDebugger.java deleted file mode 100644 index 988be8c5c1c..00000000000 --- a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/snapshot/DefaultSpanDebugger.java +++ /dev/null @@ -1,76 +0,0 @@ -package com.datadog.debugger.snapshot; - -import static com.datadog.debugger.agent.ConfigurationAcceptor.Source.SPAN_DEBUG; - -import com.datadog.debugger.agent.ConfigurationUpdater; -import com.datadog.debugger.exception.Fingerprinter; -import com.datadog.debugger.util.ClassNameFiltering; -import datadog.trace.bootstrap.debugger.DebuggerContext.SpanDebugger; -import datadog.trace.util.AgentTaskScheduler; -import datadog.trace.util.stacktrace.StackWalkerFactory; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class DefaultSpanDebugger implements SpanDebugger { - private static final Logger LOGGER = LoggerFactory.getLogger(DefaultSpanDebugger.class); - - private final SpanDebuggerProbeManager probeManager; - private final ConfigurationUpdater configurationUpdater; - private final ClassNameFiltering classNameFiltering; - - private AgentTaskScheduler taskScheduler = AgentTaskScheduler.INSTANCE; - - public DefaultSpanDebugger( - ConfigurationUpdater configurationUpdater, ClassNameFiltering classNameFiltering) { - this.probeManager = new SpanDebuggerProbeManager(classNameFiltering); - this.configurationUpdater = configurationUpdater; - this.classNameFiltering = classNameFiltering; - } - - public SpanDebuggerProbeManager probeManager() { - return probeManager; - } - - public AgentTaskScheduler taskScheduler() { - return taskScheduler; - } - - public DefaultSpanDebugger taskScheduler(AgentTaskScheduler taskScheduler) { - this.taskScheduler = taskScheduler; - return this; - } - - @Override - public String captureSnapshot(String signature) { - StackTraceElement element = findPlaceInStack(); - String fingerprint = Fingerprinter.fingerprint(element); - if (fingerprint == null) { - LOGGER.debug("Unable to fingerprint snapshot"); - return null; - } - - if (!probeManager.isAlreadyInstrumented(fingerprint)) { - String probeId = probeManager.createProbeForFrame(element, signature); - if (probeId != null) { - taskScheduler.execute( - () -> { - configurationUpdater.accept(SPAN_DEBUG, probeManager.getProbes()); - probeManager.addFingerprint(fingerprint, probeId); - }); - } - return probeId; - } - - return probeManager.getProbeId(fingerprint); - } - - private StackTraceElement findPlaceInStack() { - return StackWalkerFactory.INSTANCE.walk( - stream -> { - return stream - .filter(element -> !classNameFiltering.isExcluded(element.getClassName())) - .findFirst() - .orElse(null); - }); - } -} diff --git a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/snapshot/SpanDebug.java b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/snapshot/SpanDebug.java deleted file mode 100644 index d4d9fd5540e..00000000000 --- a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/snapshot/SpanDebug.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.datadog.debugger.snapshot; - -import datadog.trace.bootstrap.instrumentation.api.AgentSpan; -import datadog.trace.core.DDSpanContext; -import datadog.trace.core.propagation.PropagationTags; - -public final class SpanDebug { - // Granularity of debugging information we want to capture - // enums instead? - /** no debug information */ - public static final int NONE = 0; - /** source code locations (just the span origin frame) */ - public static final int ORIGIN_FRAME_ONLY = 1; - /** source code locations (full stack trace) */ - public static final int ALL_FRAMES = 2; - /** variable values (request/response bodies, enriched dynamic logs) */ - public static final int CAPTURE_ORIGIN_FRAMES = 4; - /** capture exception replay information */ - public static final int EXCEPTION_REPLAY = 8; - /** capture all user probes */ - public static final int CAPTURE_ALL_PROBES = 16; - - public static boolean isSpanDebugEnabled(AgentSpan span, int... levels) { - if (span.context() instanceof DDSpanContext) { - int debug = getDebugLevel(span); - - for (int level : levels) { - if ((debug & level) == level) { - return true; - } - } - } - - return false; - } - - private static int getDebugLevel(AgentSpan span) { - PropagationTags tags = ((DDSpanContext) span.context()).getPropagationTags(); - return tags.getDebugPropagation(); - } - - public static void enableDebug(AgentSpan span, int... levels) { - if (span.context() instanceof DDSpanContext && levels.length != 0) { - DDSpanContext context = (DDSpanContext) span.context(); - PropagationTags tags = context.getPropagationTags(); - int debug = 0; - - for (int level : levels) { - debug |= level; - } - - tags.updateDebugPropagation(debug); - } - } -} diff --git a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/snapshot/SpanDebuggerProbeManager.java b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/snapshot/SpanDebuggerProbeManager.java deleted file mode 100644 index fbdb4f1db69..00000000000 --- a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/snapshot/SpanDebuggerProbeManager.java +++ /dev/null @@ -1,58 +0,0 @@ -package com.datadog.debugger.snapshot; - -import com.datadog.debugger.probe.SpanDebuggerProbe; -import com.datadog.debugger.probe.Where; -import com.datadog.debugger.util.ClassNameFiltering; -import datadog.trace.bootstrap.debugger.ProbeId; -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; -import java.util.concurrent.ConcurrentHashMap; - -public class SpanDebuggerProbeManager { - private Map fingerprints = new HashMap<>(); - private final Map probes = new ConcurrentHashMap<>(); - private final ClassNameFiltering classNameFiltering; - - public SpanDebuggerProbeManager(ClassNameFiltering classNameFiltering) { - this.classNameFiltering = classNameFiltering; - } - - public Collection getProbes() { - return probes.values(); - } - - void addFingerprint(String fingerprint, String probeId) { - fingerprints.put(fingerprint, probeId); - } - - String getProbeId(String fingerprint) { - return fingerprints.get(fingerprint); - } - - public ClassNameFiltering getClassNameFiltering() { - return classNameFiltering; - } - - public String createProbeForFrame(StackTraceElement element, String signature) { - Where where = - Where.convertLineToMethod( - element.getClassName(), - element.getMethodName(), - signature, - String.valueOf(element.getLineNumber())); - SpanDebuggerProbe probe = - new SpanDebuggerProbe(new ProbeId(UUID.randomUUID().toString(), 0), signature, where, this); - probes.putIfAbsent(probe.getId(), probe); - return probe.getId(); - } - - public ClassNameFiltering classNameFiltering() { - return classNameFiltering; - } - - public boolean isAlreadyInstrumented(String fingerprint) { - return fingerprints.containsKey(fingerprint); - } -} 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 1228e3de62b..d7fc7bf7408 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 @@ -30,6 +30,7 @@ import com.datadog.debugger.el.ProbeCondition; import com.datadog.debugger.el.values.StringValue; import com.datadog.debugger.instrumentation.InstrumentationResult; +import com.datadog.debugger.probe.DebuggerProbe; import com.datadog.debugger.probe.LogProbe; import com.datadog.debugger.probe.MetricProbe; import com.datadog.debugger.probe.ProbeDefinition; @@ -48,7 +49,15 @@ import datadog.trace.agent.tooling.TracerInstaller; import datadog.trace.api.Config; import datadog.trace.api.interceptor.MutableSpan; -import datadog.trace.bootstrap.debugger.*; +import datadog.trace.bootstrap.debugger.CapturedContext; +import datadog.trace.bootstrap.debugger.CorrelationAccess; +import datadog.trace.bootstrap.debugger.DebuggerContext; +import datadog.trace.bootstrap.debugger.EvaluationError; +import datadog.trace.bootstrap.debugger.Limits; +import datadog.trace.bootstrap.debugger.MethodLocation; +import datadog.trace.bootstrap.debugger.ProbeId; +import datadog.trace.bootstrap.debugger.ProbeImplementation; +import datadog.trace.bootstrap.debugger.ProbeRateLimiter; import datadog.trace.bootstrap.debugger.el.ValueReferences; import datadog.trace.bootstrap.debugger.util.Redaction; import datadog.trace.core.CoreTracer; @@ -104,6 +113,7 @@ public class CapturedSnapshotTest { private static final ProbeId PROBE_ID1 = new ProbeId("beae1807-f3b0-4ea8-a74f-826790c5e6f6", 0); private static final ProbeId PROBE_ID2 = new ProbeId("beae1807-f3b0-4ea8-a74f-826790c5e6f7", 0); private static final ProbeId PROBE_ID3 = new ProbeId("beae1807-f3b0-4ea8-a74f-826790c5e6f8", 0); + private static final ProbeId PROBE_ID4 = new ProbeId("beae1807-f3b0-4ea8-a74f-826790c5e6f9", 0); private static final String SERVICE_NAME = "service-name"; private static final JsonAdapter> GENERIC_ADAPTER = MoshiHelper.createGenericAdapter(); @@ -2311,6 +2321,7 @@ public void allProbesSameMethod() throws IOException, URISyntaxException { .where(where) .build()) .add(LogProbe.builder().probeId(PROBE_ID3).where(where).build()) + .add(DebuggerProbe.builder().probeId(PROBE_ID4).where(where).build()) .build(); CoreTracer tracer = CoreTracer.builder().build(); 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 ce1c446c54f..c2a00678067 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 @@ -3,6 +3,8 @@ import static com.datadog.debugger.probe.MetricProbe.MetricKind.COUNT; import static com.datadog.debugger.probe.MetricProbe.MetricKind.GAUGE; import static com.datadog.debugger.util.LogProbeTestHelper.parseTemplate; +import static java.util.Arrays.asList; +import static java.util.stream.Collectors.toList; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; @@ -10,9 +12,9 @@ import com.datadog.debugger.el.DSL; import com.datadog.debugger.el.ProbeCondition; +import com.datadog.debugger.probe.DebuggerProbe; import com.datadog.debugger.probe.LogProbe; import com.datadog.debugger.probe.MetricProbe; -import com.datadog.debugger.probe.ProbeDefinition; import com.datadog.debugger.probe.SpanDecorationProbe; import com.datadog.debugger.probe.SpanProbe; import com.datadog.debugger.util.MoshiHelper; @@ -23,8 +25,6 @@ import java.io.IOException; import java.lang.reflect.ParameterizedType; import java.util.ArrayList; -import java.util.Arrays; -import java.util.Iterator; import java.util.List; import org.junit.jupiter.api.Test; import utils.TestHelper; @@ -34,13 +34,14 @@ public class ConfigurationTest { @Test public void getDefinitions() { Configuration config1 = createConfig1(); - assertEquals(5, config1.getDefinitions().size()); - Iterator iterator = config1.getDefinitions().iterator(); - assertEquals("metric1", iterator.next().getId()); - assertEquals("probe1", iterator.next().getId()); - assertEquals("log1", iterator.next().getId()); - assertEquals("span1", iterator.next().getId()); - assertEquals("decorateSpan1", iterator.next().getId()); + List definitions = + config1.getDefinitions().stream().map(d -> d.getId()).sorted().collect(toList()); + List list = + asList("metric1", "probe1", "log1", "span1", "debug1", "decorateSpan1").stream() + .sorted() + .collect(toList()); + + assertEquals(list, definitions); } @Test @@ -197,7 +198,7 @@ private void doCaptureDeserialization( private String serialize() { Configuration config1 = createConfig1(); Configuration config2 = createConfig2(); - List configs = new ArrayList<>(Arrays.asList(config1, config2)); + List configs = new ArrayList<>(asList(config1, config2)); ParameterizedType type = Types.newParameterizedType(List.class, Configuration.class); JsonAdapter> adapter = MoshiHelper.createMoshiConfig().adapter(type); return adapter.toJson(configs); @@ -265,11 +266,14 @@ private Configuration createConfig1() { createLog( "log1", "this is a log line with arg={arg}", "java.lang.String", "indexOf", "(String)"); SpanProbe span1 = createSpan("span1", "java.lang.String", "indexOf", "(String)"); + DebuggerProbe debuggerProbe = + createDebuggerProbe("debug1", "java.lang.String", "indexOf", "(String)"); + SpanDecorationProbe.Decoration decoration = new SpanDecorationProbe.Decoration( new ProbeCondition( DSL.when(DSL.eq(DSL.ref("arg1"), DSL.value("foo"))), "arg1 == 'foo'"), - Arrays.asList( + asList( new SpanDecorationProbe.Tag( "id", new SpanDecorationProbe.TagValue("{id}", parseTemplate("{id}"))))); SpanDecorationProbe spanDecoration1 = @@ -281,18 +285,18 @@ private Configuration createConfig1() { "indexOf", "(String)"); Configuration.FilterList allowList = - new Configuration.FilterList( - Arrays.asList("java.lang.util"), Arrays.asList("java.lang.String")); + new Configuration.FilterList(asList("java.lang.util"), asList("java.lang.String")); Configuration.FilterList denyList = new Configuration.FilterList( - Arrays.asList("java.security"), Arrays.asList("javax.security.auth.AuthPermission")); + asList("java.security"), asList("javax.security.auth.AuthPermission")); LogProbe.Sampling globalSampling = new LogProbe.Sampling(10.0); return new Configuration( "service1", - Arrays.asList(metric1), - Arrays.asList(probe1, log1), - Arrays.asList(span1), - Arrays.asList(spanDecoration1), + asList(metric1), + asList(probe1, log1), + asList(span1), + asList(debuggerProbe), + asList(spanDecoration1), allowList, denyList, globalSampling); @@ -310,10 +314,13 @@ private Configuration createConfig2() { "indexOf", "(String)"); SpanProbe span2 = createSpan("span2", "String.java", 12, 23); + DebuggerProbe debuggerProbe = + createDebuggerProbe("debug1", "String.java", "indexOf", "(String)"); + SpanDecorationProbe.Decoration decoration = new SpanDecorationProbe.Decoration( new ProbeCondition(DSL.when(DSL.eq(DSL.ref("arg"), DSL.value("foo"))), "arg == 'foo'"), - Arrays.asList( + asList( new SpanDecorationProbe.Tag( "tag1", new SpanDecorationProbe.TagValue("{arg1}", parseTemplate("{arg1}"))), new SpanDecorationProbe.Tag( @@ -327,18 +334,18 @@ private Configuration createConfig2() { "indexOf", "(String)"); Configuration.FilterList allowList = - new Configuration.FilterList( - Arrays.asList("java.lang.util"), Arrays.asList("java.lang.String")); + new Configuration.FilterList(asList("java.lang.util"), asList("java.lang.String")); Configuration.FilterList denyList = new Configuration.FilterList( - Arrays.asList("java.security"), Arrays.asList("javax.security.auth.AuthPermission")); + asList("java.security"), asList("javax.security.auth.AuthPermission")); LogProbe.Sampling globalSampling = new LogProbe.Sampling(10.0); return new Configuration( "service2", - Arrays.asList(metric2), - Arrays.asList(probe2, log2), - Arrays.asList(span2), - Arrays.asList(spanDecoration2), + asList(metric2), + asList(probe2, log2), + asList(span2), + asList(debuggerProbe), + asList(spanDecoration2), allowList, denyList, globalSampling); @@ -393,6 +400,16 @@ private static LogProbe createLog( .build(); } + private static DebuggerProbe createDebuggerProbe( + String id, String typeName, String methodName, String signature) { + return DebuggerProbe.builder() + .language("java") + .probeId(id, 0) + .where(typeName, methodName, signature) + .evaluateAt(MethodLocation.ENTRY) + .build(); + } + private static SpanProbe createSpan( String id, String typeName, String methodName, String signature) { return SpanProbe.builder() diff --git a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/DebuggerProbeInstrumentationTest.java b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/DebuggerProbeInstrumentationTest.java new file mode 100644 index 00000000000..aa01eb3dcf8 --- /dev/null +++ b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/DebuggerProbeInstrumentationTest.java @@ -0,0 +1,184 @@ +package com.datadog.debugger.agent; + +import static com.datadog.debugger.agent.SpanDecorationProbeInstrumentationTest.resolver; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static utils.InstrumentationTestHelper.compileAndLoadClass; + +import com.datadog.debugger.agent.Configuration.Builder; +import com.datadog.debugger.codeorigin.CodeOriginProbeManager; +import com.datadog.debugger.codeorigin.DefaultCodeOriginRecorder; +import com.datadog.debugger.origin.CodeOriginTest; +import com.datadog.debugger.probe.CodeOriginProbe; +import com.datadog.debugger.probe.DebuggerProbe; +import com.datadog.debugger.probe.LogProbe; +import com.datadog.debugger.probe.MetricProbe; +import com.datadog.debugger.probe.ProbeDefinition; +import com.datadog.debugger.probe.Where; +import com.datadog.debugger.sink.DebuggerSink; +import com.datadog.debugger.sink.ProbeStatusSink; +import com.datadog.debugger.util.TestTraceInterceptor; +import datadog.trace.agent.tooling.TracerInstaller; +import datadog.trace.api.Config; +import datadog.trace.api.interceptor.MutableSpan; +import datadog.trace.bootstrap.debugger.DebuggerContext; +import datadog.trace.bootstrap.debugger.ProbeId; +import datadog.trace.bootstrap.debugger.util.Redaction; +import datadog.trace.core.CoreTracer; +import datadog.trace.core.DDSpan; +import java.io.IOException; +import java.net.URISyntaxException; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; +import org.joor.Reflect; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.api.TestInstance.Lifecycle; +import org.mockito.ArgumentMatchers; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@TestInstance(Lifecycle.PER_CLASS) +public class DebuggerProbeInstrumentationTest extends ProbeInstrumentationTest { + private static final ProbeId DEBUG_PROBE_ID = new ProbeId("86753098675309", 0); + + private static final ProbeId CODE_ORIGIN_PROBE_ID = new ProbeId("48946084894608", 0); + + private static final Logger log = LoggerFactory.getLogger(DebuggerProbeInstrumentationTest.class); + + public static final CodeOriginProbeManager probeManager = + new CodeOriginProbeManager( + mock(ConfigurationUpdater.class), CodeOriginTest.classNameFiltering); + + private DefaultCodeOriginRecorder codeOriginRecorder = + new DefaultCodeOriginRecorder(probeManager); + + private TestTraceInterceptor traceInterceptor; + + @BeforeEach + public void setUp() { + traceInterceptor = new TestTraceInterceptor(); + CoreTracer tracer = CoreTracer.builder().build(); + TracerInstaller.forceInstallGlobalTracer(tracer); + tracer.addTraceInterceptor(traceInterceptor); + } + + @Override + @AfterEach + public void after() { + super.after(); + Redaction.clearUserDefinedTypes(); + } + + @Test + public void testProbeInstallation() throws IOException, URISyntaxException { + final String CLASS_NAME = "com.datadog.debugger.CapturedSnapshot20"; + installProbe(CLASS_NAME, "process", "int (java.lang.String)"); + Class testClass = compileAndLoadClass(CLASS_NAME); + int result = Reflect.on(testClass).call("main", "1").get(); + assertEquals(84, result); + result = Reflect.on(testClass).call("main", "1").get(); + assertEquals(84, result); + MutableSpan span = traceInterceptor.getFirstSpan(); + assertEquals( + DEBUG_PROBE_ID.getId(), + span.getTags().get("_dd.ld.probe_id"), + span.getTags().keySet().toString()); + String debugFlag = + ((DDSpan) span.getLocalRootSpan()).context().getPropagationTags().getDebugPropagation(); + assertEquals("1", debugFlag); + } + + @Test + public void testWithCodeOrigin() throws IOException, URISyntaxException { + final String CLASS_NAME = "com.datadog.debugger.CapturedSnapshot20"; + + Where where = + Where.convertLineToMethod(CLASS_NAME, "process", "int (java.lang.String)", (String[]) null); + installProbes( + DebuggerProbe.builder() + .probeId(DEBUG_PROBE_ID) + .where(CLASS_NAME, "process", "int (java.lang.String)") + .build(), + new CodeOriginProbe(CODE_ORIGIN_PROBE_ID, "int (java.lang.String)", where, probeManager)); + Class testClass = compileAndLoadClass(CLASS_NAME); + int result = Reflect.on(testClass).call("main", "1").get(); + assertEquals(84, result); + MutableSpan span = traceInterceptor.getFirstSpan(); + assertEquals( + DEBUG_PROBE_ID.getId(), + span.getTags().get("_dd.ld.probe_id"), + span.getTags().keySet().toString()); + String debugFlag = + ((DDSpan) span.getLocalRootSpan()).context().getPropagationTags().getDebugPropagation(); + assertEquals("1", debugFlag); + verify(probeStatusSink).addEmitting(ArgumentMatchers.eq(DEBUG_PROBE_ID)); + } + + private void installProbe(String typeName, String methodName, String signature) { + DebuggerProbe probe = + DebuggerProbe.builder() + .probeId(DEBUG_PROBE_ID) + .where(typeName, methodName, signature) + .build(); + registerConfiguration(Configuration.builder().setService(SERVICE_NAME).add(probe).build()); + } + + @SuppressWarnings({"unchecked"}) + private void installProbes(D... definitions) { + Builder builder = Configuration.builder().setService(SERVICE_NAME); + bucketProbes(definitions, builder); + registerConfiguration(builder.build()); + } + + @SuppressWarnings("unchecked") + private static void bucketProbes(D[] definitions, Builder builder) { + Arrays.stream(definitions) + .collect(Collectors.groupingBy(d -> d.getClass().getSimpleName())) + .forEach( + (key, value1) -> { + switch (key) { + case ("CodeOriginProbe"): + value1.forEach(probe -> probeManager.installProbe((CodeOriginProbe) probe)); + break; + case ("MetricProbe"): + builder.addMetricProbes((List) value1); + break; + case ("DebuggerProbe"): + builder.addDebuggerProbes((List) value1); + break; + case ("LogProbe"): + builder.addLogProbes((List) value1); + break; + default: + throw new UnsupportedOperationException(key + " is an unknown probe type"); + } + }); + } + + private void registerConfiguration(Configuration configuration) { + Config config = mock(Config.class); + when(config.isDebuggerEnabled()).thenReturn(true); + when(config.isDebuggerClassFileDumpEnabled()).thenReturn(true); + when(config.isDebuggerCodeOriginEnabled()).thenReturn(true); + when(config.getFinalDebuggerSnapshotUrl()) + .thenReturn("http://localhost:8126/debugger/v1/input"); + when(config.getFinalDebuggerSymDBUrl()).thenReturn("http://localhost:8126/symdb/v1/input"); + probeStatusSink = mock(ProbeStatusSink.class); + currentTransformer = + new DebuggerTransformer( + config, configuration, null, new DebuggerSink(config, probeStatusSink)); + instr.addTransformer(currentTransformer); + mockSink = new MockSink(config, probeStatusSink); + System.out.println("remocking mockSink"); + DebuggerAgentHelper.injectSink(mockSink); + DebuggerContext.initProbeResolver((encodedProbeId) -> resolver(encodedProbeId, configuration)); + DebuggerContext.initClassFilter(new DenyListHelper(null)); + DebuggerContext.initCodeOrigin(codeOriginRecorder); + } +} 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 288465bee1b..2ec50984f21 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 @@ -8,6 +8,7 @@ import static com.datadog.debugger.probe.SpanDecorationProbe.TargetSpan.ACTIVE; import static com.datadog.debugger.probe.SpanDecorationProbe.TargetSpan.ROOT; import static com.datadog.debugger.util.LogProbeTestHelper.parseTemplate; +import static java.util.Arrays.asList; import static java.util.Collections.singletonList; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -24,6 +25,7 @@ import com.datadog.debugger.el.expressions.BooleanExpression; import com.datadog.debugger.el.values.StringValue; import com.datadog.debugger.probe.LogProbe; +import com.datadog.debugger.probe.ProbeDefinition; import com.datadog.debugger.probe.SpanDecorationProbe; import com.datadog.debugger.sink.DebuggerSink; import com.datadog.debugger.sink.ProbeStatusSink; @@ -41,7 +43,7 @@ import datadog.trace.core.CoreTracer; import java.io.IOException; import java.net.URISyntaxException; -import java.util.Arrays; +import java.util.Collection; import java.util.List; import org.joor.Reflect; import org.junit.jupiter.api.AfterEach; @@ -100,7 +102,7 @@ public void methodActiveSpanTagList() throws IOException, URISyntaxException { installSingleSpanDecoration( CLASS_NAME, ACTIVE, - Arrays.asList(deco1, deco2, deco3, deco4, deco5), + asList(deco1, deco2, deco3, deco4, deco5), "process", "int (java.lang.String)"); Class testClass = compileAndLoadClass(CLASS_NAME); @@ -122,11 +124,7 @@ public void methodRootSpanTagList() throws IOException, URISyntaxException { SpanDecorationProbe.Decoration deco3 = createDecoration("tag3", "{strField}"); SpanDecorationProbe.Decoration deco4 = createDecoration("tag4", "{@return}"); installSingleSpanDecoration( - CLASS_NAME, - ROOT, - Arrays.asList(deco1, deco2, deco3, deco4), - "process3", - "int (java.lang.String)"); + CLASS_NAME, ROOT, asList(deco1, deco2, deco3, deco4), "process3", "int (java.lang.String)"); Class testClass = compileAndLoadClass(CLASS_NAME); int result = Reflect.on(testClass).call("main", "1").get(); assertEquals(45, result); @@ -259,7 +257,7 @@ public void lineRootSpanTagList() throws IOException, URISyntaxException { SpanDecorationProbe.Decoration deco2 = createDecoration("tag2", "{this.intField}"); SpanDecorationProbe.Decoration deco3 = createDecoration("tag3", "{strField}"); installSingleSpanDecoration( - CLASS_NAME, ROOT, Arrays.asList(deco1, deco2, deco3), "CapturedSnapshot21.java", 67); + CLASS_NAME, ROOT, asList(deco1, deco2, deco3), "CapturedSnapshot21.java", 67); Class testClass = compileAndLoadClass(CLASS_NAME); int result = Reflect.on(testClass).call("main", "1").get(); assertEquals(45, result); @@ -321,12 +319,24 @@ public void mixedWithLogProbes() throws IOException, URISyntaxException { SpanDecorationProbe.Decoration decoration2 = createDecoration("tag2", "{arg}"); SpanDecorationProbe spanDecoProbe1 = createProbeBuilder( - PROBE_ID1, ACTIVE, singletonList(decoration1), CLASS_NAME, "process", null, null) + PROBE_ID1, + ACTIVE, + singletonList(decoration1), + CLASS_NAME, + "process", + null, + (String[]) null) .evaluateAt(MethodLocation.EXIT) .build(); SpanDecorationProbe spanDecoProbe2 = createProbeBuilder( - PROBE_ID2, ACTIVE, singletonList(decoration2), CLASS_NAME, "process", null, null) + PROBE_ID2, + ACTIVE, + singletonList(decoration2), + CLASS_NAME, + "process", + null, + (String[]) null) .evaluateAt(MethodLocation.ENTRY) .build(); LogProbe logProbe1 = @@ -374,12 +384,24 @@ public void mixedEntryExit() throws IOException, URISyntaxException { SpanDecorationProbe.Decoration decoration2 = createDecoration("tag2", "{arg}"); SpanDecorationProbe spanDecoProbe1 = createProbeBuilder( - PROBE_ID1, ACTIVE, singletonList(decoration1), CLASS_NAME, "process", null, null) + PROBE_ID1, + ACTIVE, + singletonList(decoration1), + CLASS_NAME, + "process", + null, + (String[]) null) .evaluateAt(MethodLocation.EXIT) .build(); SpanDecorationProbe spanDecoProbe2 = createProbeBuilder( - PROBE_ID2, ACTIVE, singletonList(decoration2), CLASS_NAME, "process", null, null) + PROBE_ID2, + ACTIVE, + singletonList(decoration2), + CLASS_NAME, + "process", + null, + (String[]) null) .evaluateAt(MethodLocation.ENTRY) .build(); Configuration configuration = @@ -404,7 +426,7 @@ public void keywordRedaction() throws IOException, URISyntaxException { SpanDecorationProbe.Decoration decoration2 = createDecoration("tag2", "{this.password}"); SpanDecorationProbe.Decoration decoration3 = createDecoration("tag3", "{strMap['password']}"); List decorations = - Arrays.asList(decoration1, decoration2, decoration3); + asList(decoration1, decoration2, decoration3); installSingleSpanDecoration( CLASS_NAME, ACTIVE, decorations, "process", "int (java.lang.String)"); Class testClass = compileAndLoadClass(CLASS_NAME); @@ -447,7 +469,7 @@ public void keywordRedactionConditions() throws IOException, URISyntaxException "tag3", "foo"); List decorations = - Arrays.asList(decoration1, decoration2, decoration3); + asList(decoration1, decoration2, decoration3); installSingleSpanDecoration( CLASS_NAME, ACTIVE, decorations, "process", "int (java.lang.String)"); Class testClass = compileAndLoadClass(CLASS_NAME); @@ -481,7 +503,7 @@ public void typeRedaction() throws IOException, URISyntaxException { SpanDecorationProbe.Decoration decoration2 = createDecoration("tag2", "{this.creds}"); SpanDecorationProbe.Decoration decoration3 = createDecoration("tag3", "{credMap['dave']}"); List decorations = - Arrays.asList(decoration1, decoration2, decoration3); + asList(decoration1, decoration2, decoration3); installSingleSpanDecoration( CLASS_NAME, ACTIVE, decorations, "process", "int (java.lang.String)"); Class testClass = compileAndLoadClass(CLASS_NAME); @@ -533,7 +555,7 @@ public void typeRedactionConditions() throws IOException, URISyntaxException { "tag3", "foo"); List decorations = - Arrays.asList(decoration1, decoration2, decoration3); + asList(decoration1, decoration2, decoration3); installSingleSpanDecoration( CLASS_NAME, ACTIVE, decorations, "process", "int (java.lang.String)"); Class testClass = compileAndLoadClass(CLASS_NAME); @@ -558,7 +580,7 @@ public void typeRedactionConditions() throws IOException, URISyntaxException { private SpanDecorationProbe.Decoration createDecoration(String tagName, String valueDsl) { List tags = - Arrays.asList( + asList( new SpanDecorationProbe.Tag( tagName, new SpanDecorationProbe.TagValue(valueDsl, parseTemplate(valueDsl)))); return new SpanDecorationProbe.Decoration(null, tags); @@ -567,7 +589,7 @@ private SpanDecorationProbe.Decoration createDecoration(String tagName, String v private SpanDecorationProbe.Decoration createDecoration( BooleanExpression expression, String dsl, String tagName, String valueDsl) { List tags = - Arrays.asList( + asList( new SpanDecorationProbe.Tag( tagName, new SpanDecorationProbe.TagValue(valueDsl, parseTemplate(valueDsl)))); return new SpanDecorationProbe.Decoration(new ProbeCondition(DSL.when(expression), dsl), tags); @@ -579,8 +601,7 @@ private void installSingleSpanDecoration( SpanDecorationProbe.Decoration decoration, String methodName, String signature) { - installSingleSpanDecoration( - typeName, targetSpan, Arrays.asList(decoration), methodName, signature); + installSingleSpanDecoration(typeName, targetSpan, asList(decoration), methodName, signature); } private void installSingleSpanDecoration( @@ -589,7 +610,7 @@ private void installSingleSpanDecoration( SpanDecorationProbe.Decoration decoration, String sourceFile, int line) { - installSingleSpanDecoration(typeName, targetSpan, Arrays.asList(decoration), sourceFile, line); + installSingleSpanDecoration(typeName, targetSpan, asList(decoration), sourceFile, line); } private void installSingleSpanDecoration( @@ -675,7 +696,7 @@ private void installSpanProbes(String expectedClassName, SpanDecorationProbe... expectedClassName, Configuration.builder() .setService(SERVICE_NAME) - .addSpanDecorationProbes(Arrays.asList(probes)) + .addSpanDecorationProbes(asList(probes)) .build()); } @@ -697,15 +718,29 @@ private void installSpanDecorationProbes(String expectedClassName, Configuration DebuggerContext.initClassFilter(new DenyListHelper(null)); } - private ProbeImplementation resolver(String encodedProbeId, Configuration configuration) { - for (SpanDecorationProbe probe : configuration.getSpanDecorationProbes()) { - if (probe.getProbeId().getEncodedId().equals(encodedProbeId)) { + static ProbeImplementation resolver(String encodedProbeId, Configuration configuration) { + List> list1 = + asList( + configuration.getSpanDecorationProbes(), + configuration.getLogProbes(), + configuration.getDebuggerProbes()); + for (Collection list : list1) { + + ProbeImplementation probe = scanForProbe(encodedProbeId, list); + if (probe != null) { return probe; } } - for (LogProbe probe : configuration.getLogProbes()) { - if (probe.getProbeId().getEncodedId().equals(encodedProbeId)) { - return probe; + return null; + } + + private static ProbeDefinition scanForProbe( + String encodedProbeId, Collection probes) { + if (probes != null) { + for (ProbeDefinition probe : probes) { + if (probe.getProbeId().getEncodedId().equals(encodedProbeId)) { + return probe; + } } } return null; diff --git a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/code_origin/SpanDebuggerTest.java b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/code_origin/SpanDebuggerTest.java deleted file mode 100644 index 046d03e177f..00000000000 --- a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/code_origin/SpanDebuggerTest.java +++ /dev/null @@ -1,179 +0,0 @@ -package com.datadog.debugger.code_origin; - -import static datadog.trace.bootstrap.debugger.CapturedContext.EMPTY_CAPTURING_CONTEXT; -import static datadog.trace.util.AgentThreadFactory.AgentThread.TASK_SCHEDULER; -import static java.util.Collections.emptyList; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import com.datadog.debugger.agent.ConfigurationUpdater; -import com.datadog.debugger.agent.DebuggerAgentHelper; -import com.datadog.debugger.probe.LogProbe.LogStatus; -import com.datadog.debugger.probe.SpanDebuggerProbe; -import com.datadog.debugger.sink.ProbeStatusSink; -import com.datadog.debugger.snapshot.DefaultSpanDebugger; -import com.datadog.debugger.snapshot.SpanDebug; -import com.datadog.debugger.util.ClassNameFiltering; -import com.datadog.debugger.util.TestSnapshotListener; -import datadog.trace.api.Config; -import datadog.trace.api.DDTags; -import datadog.trace.bootstrap.debugger.DebuggerContext; -import datadog.trace.bootstrap.debugger.MethodLocation; -import datadog.trace.bootstrap.debugger.spanorigin.SpanOriginInfo; -import datadog.trace.bootstrap.instrumentation.api.AgentSpan; -import datadog.trace.bootstrap.instrumentation.api.AgentTracer; -import datadog.trace.bootstrap.instrumentation.api.AgentTracer.TracerAPI; -import datadog.trace.bootstrap.instrumentation.api.ScopeSource; -import datadog.trace.core.CoreTracer; -import datadog.trace.test.util.DDSpecification; -import datadog.trace.util.AgentTaskScheduler; -import java.util.Arrays; -import java.util.HashSet; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -public class SpanDebuggerTest extends DDSpecification { - private ClassNameFiltering classNameFiltering; - - private ConfigurationUpdater configurationUpdater; - - private int count = 0; - - private TestSnapshotListener listener; - - private DefaultSpanDebugger spanDebugger; - private TracerAPI tracerAPI; - - @BeforeEach - public void setUp() { - injectSysConfig("dd.trace.span.origin.enabled", "true"); - AgentTracer.registerIfAbsent(CoreTracer.builder().build()); - tracerAPI = AgentTracer.get(); - configurationUpdater = mock(ConfigurationUpdater.class); - classNameFiltering = - new ClassNameFiltering( - new HashSet<>( - Arrays.asList( - "sun", - "org.junit", - "java.", - "org.gradle", - "com.sun", - "worker.org.gradle", - "datadog", - "com.datadog.debugger.probe", - "com.datadog.debugger.snapshot"))); - spanDebugger = new DefaultSpanDebugger(configurationUpdater, classNameFiltering); - spanDebugger.taskScheduler( - new AgentTaskScheduler(TASK_SCHEDULER) { - @Override - public void execute(Runnable target) { - target.run(); - } - }); - DebuggerContext.initSpanDebugger(spanDebugger); - - listener = new TestSnapshotListener(createConfig(), mock(ProbeStatusSink.class)); - DebuggerAgentHelper.injectSink(listener); - } - - @Test - public void fingerprinting() throws InterruptedException { - - AgentSpan span1 = createProbePath1(); - SpanDebug.enableDebug(span1, SpanDebug.ALL_FRAMES); - AgentSpan span2 = createProbePath1a(); - SpanDebug.enableDebug(span2, 31); - AgentSpan span3 = createProbePath1(); - - SpanDebuggerProbe[] probes = - spanDebugger.probeManager().getProbes().toArray(new SpanDebuggerProbe[0]); - - assertEquals(2, probes.length); - SpanDebuggerProbe entryProbe; - SpanDebuggerProbe exitProbe; - if (probes[0].isEntrySpanProbe()) { - entryProbe = probes[0]; - exitProbe = probes[1]; - } else { - entryProbe = probes[1]; - exitProbe = probes[0]; - } - assertTrue(entryProbe.isEntrySpanProbe()); - assertFalse(exitProbe.isEntrySpanProbe()); - invoke(span1, entryProbe); - - assertTrue( - span1.getTags().containsKey(DDTags.DD_ENTRY_LOCATION_FILE), - span1.getTags().keySet().toString()); - assertFalse( - span1.getTags().containsKey(DDTags.DD_ENTRY_LOCATION_SNAPSHOT_ID), - span1.getTags().keySet().toString()); - - invoke(span2, exitProbe); - assertTrue( - span2.getTags().containsKey(String.format(DDTags.DD_EXIT_LOCATION_FILE, 0)), - span2.getTags().keySet().toString()); - assertTrue( - span2.getTags().containsKey(DDTags.DD_EXIT_LOCATION_SNAPSHOT_ID), - span2.getTags().keySet().toString()); - - invoke(span3, entryProbe); - - assertTrue( - span3.getTags().containsKey(DDTags.DD_ENTRY_LOCATION_FILE), - span3.getTags().keySet().toString()); - assertFalse( - span3.getTags().containsKey(DDTags.DD_ENTRY_LOCATION_SNAPSHOT_ID), - span3.getTags().keySet().toString()); - } - - private void invoke(AgentSpan span1, SpanDebuggerProbe probe) { - tracerAPI.activateSpan(span1, ScopeSource.MANUAL); - probe.evaluate(EMPTY_CAPTURING_CONTEXT, LogStatus.EMPTY_LOG_STATUS, MethodLocation.EXIT); - probe.commit(EMPTY_CAPTURING_CONTEXT, EMPTY_CAPTURING_CONTEXT, emptyList()); - } - - private AgentSpan createProbePath1() { - return createProbePath2(); - } - - private AgentSpan createProbePath1a() { - return createProbePath3(); - } - - private AgentSpan createProbePath2() { - return createProbePath3(); - } - - private AgentSpan createProbePath3() { - AgentSpan span = tracerAPI.startSpan("span debugger test", "span" + (++count)); - try { - - if (count % 2 == 1) { - SpanOriginInfo.entry(span, getClass().getDeclaredMethod("createProbePath3")); - } else { - SpanOriginInfo.exit(span); - } - - return span; - } catch (NoSuchMethodException e) { - throw new RuntimeException(e); - } - } - - public static Config createConfig() { - Config config = mock(Config.class); - when(config.isDebuggerEnabled()).thenReturn(true); - when(config.isDebuggerClassFileDumpEnabled()).thenReturn(true); - when(config.isDebuggerVerifyByteCode()).thenReturn(true); - when(config.getFinalDebuggerSnapshotUrl()) - .thenReturn("http://localhost:8126/debugger/v1/input"); - when(config.getFinalDebuggerSymDBUrl()).thenReturn("http://localhost:8126/symdb/v1/input"); - when(config.getDebuggerUploadBatchSize()).thenReturn(100); - return config; - } -} 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 new file mode 100644 index 00000000000..fe1d0fbb28a --- /dev/null +++ b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/origin/CodeOriginTest.java @@ -0,0 +1,262 @@ +package com.datadog.debugger.origin; + +import static com.datadog.debugger.agent.ConfigurationAcceptor.Source.REMOTE_CONFIG; +import static com.datadog.debugger.util.TestHelper.setFieldInConfig; +import static datadog.trace.api.DDTags.DD_STACK_CODE_ORIGIN_FRAME; +import static datadog.trace.api.DDTags.DD_STACK_CODE_ORIGIN_TYPE; +import static java.lang.String.format; +import static java.util.Arrays.asList; +import static java.util.Collections.emptyList; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static utils.InstrumentationTestHelper.compileAndLoadClass; + +import com.datadog.debugger.agent.ClassesToRetransformFinder; +import com.datadog.debugger.agent.Configuration; +import com.datadog.debugger.agent.ConfigurationUpdater; +import com.datadog.debugger.agent.DebuggerAgentHelper; +import com.datadog.debugger.agent.DebuggerTransformer; +import com.datadog.debugger.agent.DebuggerTransformer.InstrumentationListener; +import com.datadog.debugger.agent.JsonSnapshotSerializer; +import com.datadog.debugger.agent.MockSampler; +import com.datadog.debugger.codeorigin.CodeOriginProbeManager; +import com.datadog.debugger.codeorigin.DefaultCodeOriginRecorder; +import com.datadog.debugger.probe.CodeOriginProbe; +import com.datadog.debugger.sink.DebuggerSink; +import com.datadog.debugger.sink.ProbeStatusSink; +import com.datadog.debugger.util.ClassNameFiltering; +import com.datadog.debugger.util.TestSnapshotListener; +import com.datadog.debugger.util.TestTraceInterceptor; +import datadog.trace.agent.tooling.TracerInstaller; +import datadog.trace.api.Config; +import datadog.trace.api.DDTags; +import datadog.trace.api.InstrumenterConfig; +import datadog.trace.api.interceptor.MutableSpan; +import datadog.trace.bootstrap.debugger.DebuggerContext; +import datadog.trace.bootstrap.debugger.DebuggerContext.CodeOriginRecorder; +import datadog.trace.bootstrap.debugger.ProbeRateLimiter; +import datadog.trace.bootstrap.instrumentation.api.AgentScope; +import datadog.trace.bootstrap.instrumentation.api.AgentSpan; +import datadog.trace.bootstrap.instrumentation.api.AgentTracer; +import datadog.trace.bootstrap.instrumentation.api.AgentTracer.SpanBuilder; +import datadog.trace.bootstrap.instrumentation.api.AgentTracer.TracerAPI; +import datadog.trace.bootstrap.instrumentation.api.ScopeSource; +import datadog.trace.core.CoreTracer; +import java.lang.instrument.Instrumentation; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Optional; +import java.util.TreeSet; +import java.util.stream.Collectors; +import net.bytebuddy.agent.ByteBuddyAgent; +import org.joor.Reflect; +import org.junit.Assert; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class CodeOriginTest { + + private static final List CODE_ORIGIN_TAGS = + asList( + DDTags.DD_CODE_ORIGIN_FILE, + DDTags.DD_CODE_ORIGIN_LINE, + DDTags.DD_CODE_ORIGIN_METHOD, + DDTags.DD_CODE_ORIGIN_METHOD_SIGNATURE); + private static final List STACK_FRAME_TAGS = + asList( + format(DD_STACK_CODE_ORIGIN_FRAME, 0, 0, "file"), + format(DD_STACK_CODE_ORIGIN_FRAME, 0, 0, "line"), + format(DD_STACK_CODE_ORIGIN_FRAME, 0, 0, "method"), + format(DD_STACK_CODE_ORIGIN_FRAME, 0, 0, "type")); + private static final List COMBO_TAGS = new ArrayList<>(); + + private final Instrumentation instr = ByteBuddyAgent.install(); + private final TestTraceInterceptor traceInterceptor = new TestTraceInterceptor(); + + public static ClassNameFiltering classNameFiltering = + new ClassNameFiltering( + new HashSet<>( + asList( + "sun", + "org.junit", + "java.", + "org.gradle", + "com.sun", + "worker.org.gradle", + "datadog", + "com.datadog.debugger.probe", + "com.datadog.debugger.codeorigin"))); + + private CodeOriginProbeManager probeManager; + + private MockSampler probeSampler; + private MockSampler globalSampler; + + private ConfigurationUpdater configurationUpdater; + + private TracerAPI tracerAPI; + + static { + COMBO_TAGS.addAll(CODE_ORIGIN_TAGS); + COMBO_TAGS.addAll(STACK_FRAME_TAGS); + + System.out.println("****** CodeOriginTest.static initializer COMBO_TAGS = " + COMBO_TAGS); + } + + @Test + public void testParents() { + AgentSpan top = newSpan("root"); + + try (AgentScope ignored = tracerAPI.activateSpan(top, ScopeSource.MANUAL)) { + AgentSpan entry = newSpan("entry"); + + assertEquals("root", top.getLocalRootSpan().getOperationName()); + assertEquals("root", entry.getLocalRootSpan().getOperationName()); + Assert.assertNotEquals(entry, entry.getLocalRootSpan()); + } + } + + private AgentSpan newSpan(String name) { + tracerAPI = AgentTracer.get(); + SpanBuilder span = tracerAPI.buildSpan("code origin tests", name); + if (tracerAPI.activeSpan() != null) { + span.asChildOf(tracerAPI.activeSpan().context()); + } + return span.start(); + } + + @BeforeEach + public void before() { + CoreTracer tracer = CoreTracer.builder().build(); + TracerInstaller.forceInstallGlobalTracer(tracer); + tracer.addTraceInterceptor(traceInterceptor); + probeSampler = new MockSampler(); + globalSampler = new MockSampler(); + + ProbeRateLimiter.setSamplerSupplier(rate -> rate < 101 ? probeSampler : globalSampler); + ProbeRateLimiter.setGlobalSnapshotRate(1000); + // to activate the call to DebuggerContext.handleException + setFieldInConfig(Config.get(), "debuggerCodeOriginEnabled", true); + setFieldInInstrumenterConfig(InstrumenterConfig.get(), "codeOriginEnabled", true); + } + + private TestSnapshotListener setupCodeOrigin(Config config) { + ProbeStatusSink probeStatusSink = mock(ProbeStatusSink.class); + configurationUpdater = + new ConfigurationUpdater( + instr, + this::createTransformer, + config, + new DebuggerSink(config, probeStatusSink), + new ClassesToRetransformFinder()); + probeManager = new CodeOriginProbeManager(configurationUpdater, classNameFiltering); + TestSnapshotListener listener = new TestSnapshotListener(config, probeStatusSink); + DebuggerAgentHelper.injectSink(listener); + DebuggerContext.initProbeResolver(configurationUpdater); + DebuggerContext.initValueSerializer(new JsonSnapshotSerializer()); + CodeOriginRecorder originRecorder = new DefaultCodeOriginRecorder(probeManager); + DebuggerContext.initCodeOrigin(originRecorder); + configurationUpdater.accept(REMOTE_CONFIG, null); + return listener; + } + + @Test + public void onlyInstrument() throws Exception { + Config config = createConfig(); + TestSnapshotListener listener = setupCodeOrigin(config); + final String CLASS_NAME = "com.datadog.debugger.CodeOrigin01"; + Class testClass = compileAndLoadClass(CLASS_NAME); + int result = Reflect.onClass(testClass).call("main", "fullTrace").get(); + assertEquals(0, result); + Collection probes = probeManager.getProbes(); + assertEquals(2, probes.size()); + assertEquals(0, listener.snapshots.size()); + List spans = traceInterceptor.getAllTraces().get(0); + assertEquals(3, spans.size()); + assertEquals("main", spans.get(2).getLocalRootSpan().getOperationName()); + + List list = + spans.stream() + .filter(span -> !span.getOperationName().equals("exit")) + .collect(Collectors.toList()); + + for (MutableSpan span : list) { + checkForTags(span, "entry", COMBO_TAGS, emptyList()); + } + Optional exit = + spans.stream().filter(span -> span.getOperationName().equals("exit")).findFirst(); + assertTrue(exit.isPresent()); + exit.ifPresent(CodeOriginTest::checkExitSpanTags); + } + + private static void checkForTags( + MutableSpan span, String spanType, List included, List excluded) { + String keys = + format( + "Existing keys for %s: %s", + span.getOperationName(), new TreeSet<>(span.getTags().keySet())); + assertEquals(span.getTag(format(DD_STACK_CODE_ORIGIN_TYPE, 0)), spanType, keys); + for (String tag : included) { + assertNotNull(span.getTag(tag), tag + " not found. " + keys); + } + for (String tag : excluded) { + assertNull(span.getTag(tag), tag + " not found. " + keys); + } + } + + private static void checkExitSpanTags(MutableSpan span) { + String keys = + format( + "Existing keys for %s: %s", + span.getOperationName(), new TreeSet<>(span.getTags().keySet())); + + assertNull(span.getTag(DDTags.DD_CODE_ORIGIN_FILE), keys); + assertNull(span.getTag(DDTags.DD_CODE_ORIGIN_LINE), keys); + assertNull(span.getTag(DDTags.DD_CODE_ORIGIN_METHOD), keys); + assertNull(span.getTag(DDTags.DD_CODE_ORIGIN_METHOD_SIGNATURE), keys); + + assertNull(span.getTag(format(DD_STACK_CODE_ORIGIN_TYPE, 1)), keys); + assertNull(span.getTag(format(DD_STACK_CODE_ORIGIN_FRAME, 1, 0, "file"))); + assertNull(span.getTag(format(DD_STACK_CODE_ORIGIN_FRAME, 1, 0, "line"))); + assertNull(span.getTag(format(DD_STACK_CODE_ORIGIN_FRAME, 1, 0, "method"))); + assertNull(span.getTag(format(DD_STACK_CODE_ORIGIN_FRAME, 1, 0, "type"))); + } + + public static Config createConfig() { + Config config = mock(Config.class); + when(config.isDebuggerEnabled()).thenReturn(true); + when(config.isDebuggerClassFileDumpEnabled()).thenReturn(true); + when(config.isDebuggerVerifyByteCode()).thenReturn(true); + when(config.getFinalDebuggerSnapshotUrl()) + .thenReturn("http://localhost:8126/debugger/v1/input"); + when(config.getFinalDebuggerSymDBUrl()).thenReturn("http://localhost:8126/symdb/v1/input"); + when(config.getDebuggerUploadBatchSize()).thenReturn(100); + return config; + } + + private DebuggerTransformer createTransformer( + Config config, + Configuration configuration, + InstrumentationListener listener, + DebuggerSink debuggerSink) { + return new DebuggerTransformer(config, configuration, listener, debuggerSink); + } + + public void setFieldInInstrumenterConfig( + InstrumenterConfig config, String fieldName, Object value) { + try { + Field field = config.getClass().getDeclaredField(fieldName); + field.setAccessible(true); + field.set(config, value); + } catch (Throwable e) { + e.printStackTrace(); + } + } +} diff --git a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/probe/DebuggerProbeTest.java b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/probe/DebuggerProbeTest.java new file mode 100644 index 00000000000..b59e61832f3 --- /dev/null +++ b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/probe/DebuggerProbeTest.java @@ -0,0 +1,3 @@ +package com.datadog.debugger.probe; + +public class DebuggerProbeTest {} diff --git a/dd-java-agent/agent-debugger/src/test/resources/com/datadog/debugger/CodeOrigin01.java b/dd-java-agent/agent-debugger/src/test/resources/com/datadog/debugger/CodeOrigin01.java new file mode 100644 index 00000000000..b2e4dbb8804 --- /dev/null +++ b/dd-java-agent/agent-debugger/src/test/resources/com/datadog/debugger/CodeOrigin01.java @@ -0,0 +1,72 @@ +package com.datadog.debugger; + +import datadog.trace.api.Trace; +import datadog.trace.bootstrap.debugger.spanorigin.CodeOriginInfo; +import datadog.trace.bootstrap.instrumentation.api.AgentScope; +import datadog.trace.bootstrap.instrumentation.api.AgentSpan; +import datadog.trace.bootstrap.instrumentation.api.AgentTracer; +import datadog.trace.bootstrap.instrumentation.api.AgentTracer.SpanBuilder; +import datadog.trace.bootstrap.instrumentation.api.AgentTracer.TracerAPI; +import datadog.trace.bootstrap.instrumentation.api.ScopeSource; +import datadog.trace.bootstrap.instrumentation.api.Tags; + +public class CodeOrigin01 { + private int intField = 42; + + private static TracerAPI tracerAPI = AgentTracer.get(); + + public static int main(String arg) throws ReflectiveOperationException { + AgentSpan span = newSpan("main"); + try (AgentScope scope = tracerAPI.activateSpan(span, ScopeSource.MANUAL)) { + if (arg.equals("fullTrace")) { + return new CodeOrigin01().fullTrace(); + } else if (arg.equals("debug_1")) { + span.setTag(Tags.PROPAGATED_DEBUG, "1"); + return new CodeOrigin01().fullTrace(); + } else if (arg.equals("debug_0")) { + span.setTag(Tags.PROPAGATED_DEBUG, "0"); + return new CodeOrigin01().fullTrace(); + } + } finally { + span.finish(); + } + + return -1; + } + + private int fullTrace() throws NoSuchMethodException { + AgentSpan span = newSpan("entry"); + try(AgentScope scope = tracerAPI.activateSpan(span, ScopeSource.MANUAL)) { + entry(); + } finally { + span.finish(); + } + exit(); + + return 0; + } + + private static AgentSpan newSpan(String name) { + return tracerAPI.buildSpan("code origin tests", name).start(); + } + + public void entry() throws NoSuchMethodException { + CodeOriginInfo.entry(CodeOrigin01.class.getMethod("entry")); + // just to fill out the method body + boolean dummyCode = true; + if (!dummyCode) { + dummyCode = false; + } + } + + private int exit() { + AgentSpan span = newSpan("exit"); + try(AgentScope scope = tracerAPI.activateSpan(span, ScopeSource.MANUAL)) { + CodeOriginInfo.exit(); + return 42; + } finally { + span.finish(); + } + } + +} diff --git a/dd-java-agent/agent-jmxfetch/integrations-core b/dd-java-agent/agent-jmxfetch/integrations-core index 5240f2a7cdc..45e2bed15b1 160000 --- a/dd-java-agent/agent-jmxfetch/integrations-core +++ b/dd-java-agent/agent-jmxfetch/integrations-core @@ -1 +1 @@ -Subproject commit 5240f2a7cdcabc6ae7787b9191b9189438671f3e +Subproject commit 45e2bed15b147a28b2c3994497945c6046c6025f diff --git a/dd-java-agent/instrumentation/span-origin/src/main/java/datadog/trace/instrumentation/span_origin/EntrySpanOriginInstrumentation.java b/dd-java-agent/instrumentation/span-origin/src/main/java/datadog/trace/instrumentation/codeorigin/CodeOriginInstrumentation.java similarity index 78% rename from dd-java-agent/instrumentation/span-origin/src/main/java/datadog/trace/instrumentation/span_origin/EntrySpanOriginInstrumentation.java rename to dd-java-agent/instrumentation/span-origin/src/main/java/datadog/trace/instrumentation/codeorigin/CodeOriginInstrumentation.java index 8b768e52d00..7e07c03bfbb 100644 --- a/dd-java-agent/instrumentation/span-origin/src/main/java/datadog/trace/instrumentation/span_origin/EntrySpanOriginInstrumentation.java +++ b/dd-java-agent/instrumentation/span-origin/src/main/java/datadog/trace/instrumentation/codeorigin/CodeOriginInstrumentation.java @@ -1,4 +1,4 @@ -package datadog.trace.instrumentation.span_origin; +package datadog.trace.instrumentation.codeorigin; import datadog.trace.agent.tooling.Instrumenter.ForTypeHierarchy; import datadog.trace.agent.tooling.InstrumenterModule.Tracing; @@ -12,19 +12,19 @@ import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.matcher.ElementMatcher; -public abstract class EntrySpanOriginInstrumentation extends Tracing implements ForTypeHierarchy { +public abstract class CodeOriginInstrumentation extends Tracing implements ForTypeHierarchy { private final OneOf matcher; @SuppressForbidden - public EntrySpanOriginInstrumentation(String instrumentationName) { + public CodeOriginInstrumentation(String instrumentationName) { super(instrumentationName); this.matcher = NameMatchers.namedOneOf(getAnnotations()); } @Override public boolean isEnabled() { - return InstrumenterConfig.get().isSpanOriginEnabled() && super.isEnabled(); + return InstrumenterConfig.get().isCodeOriginEnabled() && super.isEnabled(); } protected abstract Set getAnnotations(); @@ -43,6 +43,6 @@ public ElementMatcher hierarchyMatcher() { public void methodAdvice(MethodTransformer transformer) { transformer.applyAdvice( HierarchyMatchers.isAnnotatedWith(matcher), - "datadog.trace.instrumentation.span_origin.EntrySpanOriginAdvice"); + "datadog.trace.instrumentation.codeorigin.EntrySpanOriginAdvice"); } } diff --git a/dd-java-agent/instrumentation/span-origin/src/main/java/datadog/trace/instrumentation/codeorigin/EntrySpanOriginAdvice.java b/dd-java-agent/instrumentation/span-origin/src/main/java/datadog/trace/instrumentation/codeorigin/EntrySpanOriginAdvice.java new file mode 100644 index 00000000000..273f92292b4 --- /dev/null +++ b/dd-java-agent/instrumentation/span-origin/src/main/java/datadog/trace/instrumentation/codeorigin/EntrySpanOriginAdvice.java @@ -0,0 +1,13 @@ +package datadog.trace.instrumentation.codeorigin; + +import datadog.trace.bootstrap.debugger.spanorigin.CodeOriginInfo; +import java.lang.reflect.Method; +import net.bytebuddy.asm.Advice; + +public class EntrySpanOriginAdvice { + + @Advice.OnMethodEnter + public static void onEnter(@Advice.Origin final Method method) { + CodeOriginInfo.entry(method); + } +} diff --git a/dd-java-agent/instrumentation/span-origin/src/main/java/datadog/trace/instrumentation/span_origin/EntrySpanOriginAdvice.java b/dd-java-agent/instrumentation/span-origin/src/main/java/datadog/trace/instrumentation/span_origin/EntrySpanOriginAdvice.java deleted file mode 100644 index e02e2dbb125..00000000000 --- a/dd-java-agent/instrumentation/span-origin/src/main/java/datadog/trace/instrumentation/span_origin/EntrySpanOriginAdvice.java +++ /dev/null @@ -1,14 +0,0 @@ -package datadog.trace.instrumentation.span_origin; - -import datadog.trace.bootstrap.debugger.spanorigin.SpanOriginInfo; -import datadog.trace.bootstrap.instrumentation.api.AgentTracer; -import java.lang.reflect.Method; -import net.bytebuddy.asm.Advice; - -public class EntrySpanOriginAdvice { - - @Advice.OnMethodEnter - public static void onEnter(@Advice.Origin final Method method) { - SpanOriginInfo.entry(AgentTracer.get().activeScope().span(), method); - } -} diff --git a/dd-java-agent/instrumentation/spring-boot/src/main/java/datadog/trace/instrumentation/springboot/SBEntrySpanOriginInstrumentation.java b/dd-java-agent/instrumentation/spring-boot/src/main/java/datadog/trace/instrumentation/springboot/SBCodeOriginInstrumentation.java similarity index 77% rename from dd-java-agent/instrumentation/spring-boot/src/main/java/datadog/trace/instrumentation/springboot/SBEntrySpanOriginInstrumentation.java rename to dd-java-agent/instrumentation/spring-boot/src/main/java/datadog/trace/instrumentation/springboot/SBCodeOriginInstrumentation.java index 3035092b685..1c4d1c47ea5 100644 --- a/dd-java-agent/instrumentation/spring-boot/src/main/java/datadog/trace/instrumentation/springboot/SBEntrySpanOriginInstrumentation.java +++ b/dd-java-agent/instrumentation/spring-boot/src/main/java/datadog/trace/instrumentation/springboot/SBCodeOriginInstrumentation.java @@ -4,15 +4,15 @@ import com.google.auto.service.AutoService; import datadog.trace.agent.tooling.InstrumenterModule; -import datadog.trace.instrumentation.span_origin.EntrySpanOriginInstrumentation; +import datadog.trace.instrumentation.codeorigin.CodeOriginInstrumentation; import java.util.HashSet; import java.util.Set; @AutoService(InstrumenterModule.class) -public class SBEntrySpanOriginInstrumentation extends EntrySpanOriginInstrumentation { +public class SBCodeOriginInstrumentation extends CodeOriginInstrumentation { private static final String WEB_BIND_ANNOTATION = "org.springframework.web.bind.annotation."; - public SBEntrySpanOriginInstrumentation() { + public SBCodeOriginInstrumentation() { super("spring-boot-span-origin"); } diff --git a/dd-java-agent/instrumentation/spring-data-1.8/src/main/java/datadog/trace/instrumentation/springdata/SpringDataDecorator.java b/dd-java-agent/instrumentation/spring-data-1.8/src/main/java/datadog/trace/instrumentation/springdata/SpringDataDecorator.java index f4fe2c59e81..b2e08190116 100644 --- a/dd-java-agent/instrumentation/spring-data-1.8/src/main/java/datadog/trace/instrumentation/springdata/SpringDataDecorator.java +++ b/dd-java-agent/instrumentation/spring-data-1.8/src/main/java/datadog/trace/instrumentation/springdata/SpringDataDecorator.java @@ -3,7 +3,7 @@ package datadog.trace.instrumentation.springdata; import datadog.trace.api.Config; -import datadog.trace.bootstrap.debugger.spanorigin.SpanOriginInfo; +import datadog.trace.bootstrap.debugger.spanorigin.CodeOriginInfo; import datadog.trace.bootstrap.instrumentation.api.AgentSpan; import datadog.trace.bootstrap.instrumentation.api.UTF8BytesString; import datadog.trace.bootstrap.instrumentation.decorator.ClientDecorator; @@ -46,6 +46,6 @@ public void onOperation( span.setResourceName(spanNameForMethod(method)); } - SpanOriginInfo.exit(span); + CodeOriginInfo.exit(); } } diff --git a/dd-java-agent/instrumentation/trace-annotation/src/main/java/datadog/trace/instrumentation/trace_annotation/TraceDecorator.java b/dd-java-agent/instrumentation/trace-annotation/src/main/java/datadog/trace/instrumentation/trace_annotation/TraceDecorator.java index fb7b594e69f..fe4c5654b0b 100644 --- a/dd-java-agent/instrumentation/trace-annotation/src/main/java/datadog/trace/instrumentation/trace_annotation/TraceDecorator.java +++ b/dd-java-agent/instrumentation/trace-annotation/src/main/java/datadog/trace/instrumentation/trace_annotation/TraceDecorator.java @@ -4,7 +4,7 @@ import datadog.trace.api.InstrumenterConfig; import datadog.trace.api.Trace; -import datadog.trace.bootstrap.debugger.spanorigin.SpanOriginInfo; +import datadog.trace.bootstrap.debugger.spanorigin.CodeOriginInfo; import datadog.trace.bootstrap.instrumentation.api.AgentSpan; import datadog.trace.bootstrap.instrumentation.api.UTF8BytesString; import datadog.trace.bootstrap.instrumentation.decorator.AsyncResultDecorator; @@ -91,7 +91,7 @@ public AgentSpan startMethodSpan(Method method) { afterStart(span); span.setResourceName(resourceName); - SpanOriginInfo.entry(span, method); + CodeOriginInfo.entry(method); if (measured || InstrumenterConfig.get().isMethodMeasured(method)) { span.setMeasured(true); } diff --git a/dd-java-agent/testing/src/main/groovy/datadog/trace/agent/test/asserts/TagsAssert.groovy b/dd-java-agent/testing/src/main/groovy/datadog/trace/agent/test/asserts/TagsAssert.groovy index 3cdfac3271e..b66fcd730bc 100644 --- a/dd-java-agent/testing/src/main/groovy/datadog/trace/agent/test/asserts/TagsAssert.groovy +++ b/dd-java-agent/testing/src/main/groovy/datadog/trace/agent/test/asserts/TagsAssert.groovy @@ -62,10 +62,10 @@ class TagsAssert { assertedTags.add(DDTags.PROFILING_ENABLED) assertedTags.add(DDTags.PROFILING_CONTEXT_ENGINE) assertedTags.add(DDTags.BASE_SERVICE) - assertedTags.add(DDTags.DD_ENTRY_LOCATION_FILE) - assertedTags.add(DDTags.DD_ENTRY_METHOD) - assertedTags.add(DDTags.DD_ENTRY_LINE) - assertedTags.add(DDTags.DD_ENTRY_METHOD_SIGNATURE) + assertedTags.add(DDTags.DD_CODE_ORIGIN_FILE) + assertedTags.add(DDTags.DD_CODE_ORIGIN_METHOD) + assertedTags.add(DDTags.DD_CODE_ORIGIN_LINE) + assertedTags.add(DDTags.DD_CODE_ORIGIN_METHOD_SIGNATURE) assertedTags.add(DDTags.DSM_ENABLED) assertedTags.add(DDTags.DJM_ENABLED) assertedTags.add(DDTags.PARENT_ID) diff --git a/dd-trace-api/src/main/java/datadog/trace/api/ConfigDefaults.java b/dd-trace-api/src/main/java/datadog/trace/api/ConfigDefaults.java index 78b59f153ea..f4e3357f78b 100644 --- a/dd-trace-api/src/main/java/datadog/trace/api/ConfigDefaults.java +++ b/dd-trace-api/src/main/java/datadog/trace/api/ConfigDefaults.java @@ -47,7 +47,7 @@ public final class ConfigDefaults { static final boolean DEFAULT_WRITER_BAGGAGE_INJECT = true; static final String DEFAULT_SITE = "datadoghq.com"; - static final boolean DEFAULT_TRACE_SPAN_ORIGIN_ENABLED = false; + static final boolean DEFAULT_CODE_ORIGIN_FOR_SPANS_ENABLED = false; static final boolean DEFAULT_TRACE_SPAN_ORIGIN_ENRICHED = false; static final boolean DEFAULT_TRACE_ENABLED = true; public static final boolean DEFAULT_TRACE_OTEL_ENABLED = false; @@ -180,7 +180,7 @@ public final class ConfigDefaults { static final int DEFAULT_DEBUGGER_MAX_EXCEPTION_PER_SECOND = 100; static final boolean DEFAULT_DEBUGGER_EXCEPTION_ONLY_LOCAL_ROOT = true; static final int DEFAULT_DEBUGGER_EXCEPTION_MAX_CAPTURED_FRAMES = 3; - static final boolean DEFAULT_DEBUGGER_SPAN_DEBUG_ENABLED = false; + static final boolean DEFAULT_DEBUGGER_CODE_ORIGIN_ENABLED = false; static final boolean DEFAULT_TRACE_REPORT_HOSTNAME = false; static final String DEFAULT_TRACE_ANNOTATIONS = null; diff --git a/dd-trace-api/src/main/java/datadog/trace/api/DDTags.java b/dd-trace-api/src/main/java/datadog/trace/api/DDTags.java index c9902a2bee2..a9cb08bafe1 100644 --- a/dd-trace-api/src/main/java/datadog/trace/api/DDTags.java +++ b/dd-trace-api/src/main/java/datadog/trace/api/DDTags.java @@ -2,17 +2,26 @@ public class DDTags { - public static final String DD_ENTRY_LOCATION_FILE = "_dd.entry_location.file"; - public static final String DD_ENTRY_METHOD = "_dd.entry_location.method"; - public static final String DD_ENTRY_METHOD_SIGNATURE = "_dd.entry_location.signature"; - public static final String DD_ENTRY_LINE = "_dd.entry_location.line"; - public static final String DD_ENTRY_LOCATION_SNAPSHOT_ID = "_dd.entry_location.snapshot_id"; - public static final String DD_ENTRY_TYPE = "_dd.entry_location.type"; - public static final String DD_EXIT_LOCATION_FILE = "_dd.exit_location.%d.file"; - public static final String DD_EXIT_LOCATION_LINE = "_dd.exit_location.%d.line"; - public static final String DD_EXIT_LOCATION_METHOD = "_dd.exit_location.%d.method"; - public static final String DD_EXIT_LOCATION_SNAPSHOT_ID = "_dd.exit_location.snapshot_id"; - public static final String DD_EXIT_LOCATION_TYPE = "_dd.exit_location.%d.type"; + private static final String DD_LD_PREFIX = "_dd.ld."; + + public static final String DD_STACK_CODE_ORIGIN = "_dd.stack.code_origin.%d."; + public static final String DD_STACK_CODE_ORIGIN_TYPE = DD_STACK_CODE_ORIGIN + "type"; + // _dd.stack.code_origin.frame.%d.file|line|method|type|snapshot_id + public static final String DD_STACK_CODE_ORIGIN_FRAME = DD_STACK_CODE_ORIGIN + "frame.%d.%s"; + + public static final String DD_CODE_ORIGIN_FILE = DD_LD_PREFIX + "code_origin.file"; + public static final String DD_CODE_ORIGIN_METHOD = DD_LD_PREFIX + "code_origin.method"; + public static final String DD_CODE_ORIGIN_METHOD_SIGNATURE = + DD_LD_PREFIX + "code_origin.signature"; + public static final String DD_CODE_ORIGIN_LINE = DD_LD_PREFIX + "code_origin.line"; + public static final String DD_CODE_ORIGIN_SNAPSHOT_ID = DD_LD_PREFIX + "code_origin.snapshot_id"; + public static final String DD_CODE_ORIGIN_TYPE = DD_LD_PREFIX + "code_origin.type"; + + public static final String DD_CODE_ORIGIN_EXIT_SPAN_FILE = + DD_LD_PREFIX + "code_origin.exit_span.%d.file"; + + public static final String DD_CODE_ORIGIN_EXIT_SPAN_SNAPSHOT_ID = + DD_LD_PREFIX + "code_origin.exit_span.%d.snapshot_id"; public static final String SPAN_TYPE = "span.type"; public static final String SERVICE_NAME = "service.name"; diff --git a/dd-trace-api/src/main/java/datadog/trace/api/config/DebuggerConfig.java b/dd-trace-api/src/main/java/datadog/trace/api/config/DebuggerConfig.java index e8e8d3f26c9..769e22b7d82 100644 --- a/dd-trace-api/src/main/java/datadog/trace/api/config/DebuggerConfig.java +++ b/dd-trace-api/src/main/java/datadog/trace/api/config/DebuggerConfig.java @@ -32,7 +32,7 @@ public final class DebuggerConfig { public static final String DEBUGGER_SYMBOL_INCLUDES = "symbol.database.includes"; public static final String DEBUGGER_SYMBOL_FLUSH_THRESHOLD = "symbol.database.flush.threshold"; public static final String DEBUGGER_EXCEPTION_ENABLED = "exception.debugging.enabled"; - public static final String DEBUGGER_SPAN_DEBUG_ENABLED = "span.debug.enabled"; + public static final String DEBUGGER_CODE_ORIGIN_ENABLED = "code.origin.enabled"; public static final String EXCEPTION_REPLAY_ENABLED = "exception.replay.enabled"; public static final String DEBUGGER_MAX_EXCEPTION_PER_SECOND = "exception.replay.max.exception.analysis.limit"; diff --git a/dd-trace-api/src/main/java/datadog/trace/api/config/TraceInstrumentationConfig.java b/dd-trace-api/src/main/java/datadog/trace/api/config/TraceInstrumentationConfig.java index 818b740b918..94fa0c64d1f 100644 --- a/dd-trace-api/src/main/java/datadog/trace/api/config/TraceInstrumentationConfig.java +++ b/dd-trace-api/src/main/java/datadog/trace/api/config/TraceInstrumentationConfig.java @@ -9,8 +9,7 @@ * @see TracerConfig for more tracer config options */ public final class TraceInstrumentationConfig { - public static final String TRACE_SPAN_ORIGIN_ENABLED = "trace.span.origin.enabled"; - public static final String TRACE_SPAN_ORIGIN_ENRICHED = "trace.span.origin.enriched"; + public static final String CODE_ORIGIN_FOR_SPANS_ENABLED = "code.origin.enabled"; public static final String TRACE_ENABLED = "trace.enabled"; public static final String TRACE_OTEL_ENABLED = "trace.otel.enabled"; public static final String INTEGRATIONS_ENABLED = "integrations.enabled"; diff --git a/dd-trace-core/src/main/java/datadog/trace/core/DDSpanContext.java b/dd-trace-core/src/main/java/datadog/trace/core/DDSpanContext.java index b109ce356c0..8f74debae8a 100644 --- a/dd-trace-core/src/main/java/datadog/trace/core/DDSpanContext.java +++ b/dd-trace-core/src/main/java/datadog/trace/core/DDSpanContext.java @@ -515,7 +515,7 @@ public void updateAppsecPropagation(boolean value) { propagationTags.updateAppsecPropagation(value); } - public void updateDebugPropagation(int value) { + public void updateDebugPropagation(String value) { propagationTags.updateDebugPropagation(value); } diff --git a/dd-trace-core/src/main/java/datadog/trace/core/propagation/PropagationTags.java b/dd-trace-core/src/main/java/datadog/trace/core/propagation/PropagationTags.java index 6ac676ffad7..b770ac703c7 100644 --- a/dd-trace-core/src/main/java/datadog/trace/core/propagation/PropagationTags.java +++ b/dd-trace-core/src/main/java/datadog/trace/core/propagation/PropagationTags.java @@ -105,9 +105,9 @@ public interface Factory { public abstract boolean isAppsecPropagationEnabled(); - public abstract void updateDebugPropagation(int level); + public abstract void updateDebugPropagation(String value); - public abstract int getDebugPropagation(); + public abstract String getDebugPropagation(); public HashMap createTagMap() { HashMap result = new HashMap<>(); diff --git a/dd-trace-core/src/main/java/datadog/trace/core/propagation/ptags/PTagsCodec.java b/dd-trace-core/src/main/java/datadog/trace/core/propagation/ptags/PTagsCodec.java index 86a849b1948..31796025b91 100644 --- a/dd-trace-core/src/main/java/datadog/trace/core/propagation/ptags/PTagsCodec.java +++ b/dd-trace-core/src/main/java/datadog/trace/core/propagation/ptags/PTagsCodec.java @@ -41,10 +41,8 @@ static String headerValue(PTagsCodec codec, PTags ptags) { if (ptags.isAppsecPropagationEnabled()) { size = codec.appendTag(sb, APPSEC_TAG, APPSEC_ENABLED_TAG_VALUE, size); } - if (ptags.getDebugPropagation() != PTagsFactory.DEBUG_PROPAGATION_DEFAULT) { - size = - codec.appendTag( - sb, DEBUG_TAG, TagValue.from(String.valueOf(ptags.getDebugPropagation())), size); + if (ptags.getDebugPropagation() != null) { + size = codec.appendTag(sb, DEBUG_TAG, TagValue.from(ptags.getDebugPropagation()), size); } Iterator it = ptags.getTagPairs().iterator(); while (it.hasNext() && !codec.isTooLarge(sb, size)) { @@ -94,6 +92,10 @@ static void fillTagMap(PTags propagationTags, Map tagMap) { APPSEC_TAG.forType(Encoding.DATADOG).toString(), APPSEC_ENABLED_TAG_VALUE.forType(Encoding.DATADOG).toString()); } + if (propagationTags.getDebugPropagation() != null) { + tagMap.put( + DEBUG_TAG.forType(Encoding.DATADOG).toString(), propagationTags.getDebugPropagation()); + } if (propagationTags.getTraceIdHighOrderBitsHexTagValue() != null) { tagMap.put( TRACE_ID_TAG.forType(Encoding.DATADOG).toString(), diff --git a/dd-trace-core/src/main/java/datadog/trace/core/propagation/ptags/PTagsFactory.java b/dd-trace-core/src/main/java/datadog/trace/core/propagation/ptags/PTagsFactory.java index 27daebe5ccc..fe111d7ea50 100644 --- a/dd-trace-core/src/main/java/datadog/trace/core/propagation/ptags/PTagsFactory.java +++ b/dd-trace-core/src/main/java/datadog/trace/core/propagation/ptags/PTagsFactory.java @@ -23,8 +23,6 @@ public class PTagsFactory implements PropagationTags.Factory { static final String PROPAGATION_ERROR_TAG_KEY = "_dd.propagation_error"; - static final int DEBUG_PROPAGATION_DEFAULT = 2; - private final EnumMap DEC_ENC_MAP = new EnumMap<>(HeaderType.class); private final int xDatadogTagsLimit; @@ -84,7 +82,7 @@ static class PTags extends PropagationTags { private volatile TagValue decisionMakerTagValue; private volatile boolean appsecPropagationEnabled; - private volatile int debugPropagation = DEBUG_PROPAGATION_DEFAULT; + private volatile String debugPropagation; // xDatadogTagsSize of the tagPairs, does not include the decision maker tag private volatile int xDatadogTagsSize = -1; @@ -221,12 +219,12 @@ public boolean isAppsecPropagationEnabled() { } @Override - public void updateDebugPropagation(int level) { - debugPropagation = level; + public void updateDebugPropagation(String value) { + debugPropagation = value; } @Override - public int getDebugPropagation() { + public String getDebugPropagation() { return debugPropagation; } diff --git a/dd-trace-core/src/main/java/datadog/trace/core/taginterceptor/TagInterceptor.java b/dd-trace-core/src/main/java/datadog/trace/core/taginterceptor/TagInterceptor.java index 854962eae3e..8229c5b42a9 100644 --- a/dd-trace-core/src/main/java/datadog/trace/core/taginterceptor/TagInterceptor.java +++ b/dd-trace-core/src/main/java/datadog/trace/core/taginterceptor/TagInterceptor.java @@ -112,7 +112,7 @@ public boolean interceptTag(DDSpanContext span, String tag, Object value) { span.updateAppsecPropagation(asBoolean(value)); return true; case Tags.PROPAGATED_DEBUG: - span.updateDebugPropagation(asInt(value)); + span.updateDebugPropagation(String.valueOf(value)); return true; case InstrumentationTags.SERVLET_CONTEXT: return interceptServletContext(span, value); @@ -344,18 +344,6 @@ private static boolean asBoolean(Object value) { || (!Boolean.FALSE.equals(value) && Boolean.parseBoolean(String.valueOf(value))); } - private static int asInt(Object value) { - if (value instanceof Number) { - return ((Number) value).intValue(); - } else if (value instanceof String) { - try { - return Integer.parseInt((String) value); - } catch (NumberFormatException ignore) { - } - } - return -1; - } - private static Number getOrTryParse(Object value) { if (value instanceof Number) { return (Number) value; diff --git a/internal-api/src/main/java/datadog/trace/api/Config.java b/internal-api/src/main/java/datadog/trace/api/Config.java index 9626ed1afb0..935bb902eb6 100644 --- a/internal-api/src/main/java/datadog/trace/api/Config.java +++ b/internal-api/src/main/java/datadog/trace/api/Config.java @@ -45,6 +45,7 @@ import static datadog.trace.api.ConfigDefaults.DEFAULT_DB_DBM_PROPAGATION_MODE_MODE; import static datadog.trace.api.ConfigDefaults.DEFAULT_DEBUGGER_CAPTURE_TIMEOUT; import static datadog.trace.api.ConfigDefaults.DEFAULT_DEBUGGER_CLASSFILE_DUMP_ENABLED; +import static datadog.trace.api.ConfigDefaults.DEFAULT_DEBUGGER_CODE_ORIGIN_ENABLED; import static datadog.trace.api.ConfigDefaults.DEFAULT_DEBUGGER_DIAGNOSTICS_INTERVAL; import static datadog.trace.api.ConfigDefaults.DEFAULT_DEBUGGER_ENABLED; import static datadog.trace.api.ConfigDefaults.DEFAULT_DEBUGGER_EXCEPTION_ENABLED; @@ -55,7 +56,6 @@ import static datadog.trace.api.ConfigDefaults.DEFAULT_DEBUGGER_MAX_PAYLOAD_SIZE; import static datadog.trace.api.ConfigDefaults.DEFAULT_DEBUGGER_METRICS_ENABLED; import static datadog.trace.api.ConfigDefaults.DEFAULT_DEBUGGER_POLL_INTERVAL; -import static datadog.trace.api.ConfigDefaults.DEFAULT_DEBUGGER_SPAN_DEBUG_ENABLED; import static datadog.trace.api.ConfigDefaults.DEFAULT_DEBUGGER_SYMBOL_ENABLED; import static datadog.trace.api.ConfigDefaults.DEFAULT_DEBUGGER_SYMBOL_FLUSH_THRESHOLD; import static datadog.trace.api.ConfigDefaults.DEFAULT_DEBUGGER_SYMBOL_FORCE_UPLOAD; @@ -221,6 +221,7 @@ import static datadog.trace.api.config.CwsConfig.CWS_TLS_REFRESH; import static datadog.trace.api.config.DebuggerConfig.DEBUGGER_CAPTURE_TIMEOUT; import static datadog.trace.api.config.DebuggerConfig.DEBUGGER_CLASSFILE_DUMP_ENABLED; +import static datadog.trace.api.config.DebuggerConfig.DEBUGGER_CODE_ORIGIN_ENABLED; import static datadog.trace.api.config.DebuggerConfig.DEBUGGER_DIAGNOSTICS_INTERVAL; import static datadog.trace.api.config.DebuggerConfig.DEBUGGER_ENABLED; import static datadog.trace.api.config.DebuggerConfig.DEBUGGER_EXCEPTION_ENABLED; @@ -236,7 +237,6 @@ import static datadog.trace.api.config.DebuggerConfig.DEBUGGER_REDACTED_IDENTIFIERS; import static datadog.trace.api.config.DebuggerConfig.DEBUGGER_REDACTED_TYPES; import static datadog.trace.api.config.DebuggerConfig.DEBUGGER_REDACTION_EXCLUDED_IDENTIFIERS; -import static datadog.trace.api.config.DebuggerConfig.DEBUGGER_SPAN_DEBUG_ENABLED; import static datadog.trace.api.config.DebuggerConfig.DEBUGGER_SYMBOL_ENABLED; import static datadog.trace.api.config.DebuggerConfig.DEBUGGER_SYMBOL_FLUSH_THRESHOLD; import static datadog.trace.api.config.DebuggerConfig.DEBUGGER_SYMBOL_FORCE_UPLOAD; @@ -883,7 +883,7 @@ public static String getHostName() { private final int debuggerMaxExceptionPerSecond; private final boolean debuggerExceptionOnlyLocalRoot; private final int debuggerExceptionMaxCapturedFrames; - private final boolean debuggerSpanDebugEnabled; + private final boolean debuggerCodeOriginEnabled; private final Set debuggerThirdPartyIncludes; private final Set debuggerThirdPartyExcludes; @@ -1989,8 +1989,9 @@ PROFILING_DATADOG_PROFILER_ENABLED, isDatadogProfilerSafeInCurrentEnvironment()) DEBUGGER_EXCEPTION_ENABLED, DEFAULT_DEBUGGER_EXCEPTION_ENABLED, EXCEPTION_REPLAY_ENABLED); - debuggerSpanDebugEnabled = - configProvider.getBoolean(DEBUGGER_SPAN_DEBUG_ENABLED, DEFAULT_DEBUGGER_SPAN_DEBUG_ENABLED); + debuggerCodeOriginEnabled = + configProvider.getBoolean( + DEBUGGER_CODE_ORIGIN_ENABLED, DEFAULT_DEBUGGER_CODE_ORIGIN_ENABLED); debuggerMaxExceptionPerSecond = configProvider.getInteger( DEBUGGER_MAX_EXCEPTION_PER_SECOND, DEFAULT_DEBUGGER_MAX_EXCEPTION_PER_SECOND); @@ -3392,8 +3393,8 @@ public int getDebuggerExceptionMaxCapturedFrames() { return debuggerExceptionMaxCapturedFrames; } - public boolean isDebuggerSpanDebugEnabled() { - return debuggerSpanDebugEnabled; + public boolean isDebuggerCodeOriginEnabled() { + return debuggerCodeOriginEnabled; } public Set getThirdPartyIncludes() { diff --git a/internal-api/src/main/java/datadog/trace/api/InstrumenterConfig.java b/internal-api/src/main/java/datadog/trace/api/InstrumenterConfig.java index ff6762f2019..f0aa03e83f8 100644 --- a/internal-api/src/main/java/datadog/trace/api/InstrumenterConfig.java +++ b/internal-api/src/main/java/datadog/trace/api/InstrumenterConfig.java @@ -2,6 +2,7 @@ import static datadog.trace.api.ConfigDefaults.DEFAULT_APPSEC_ENABLED; import static datadog.trace.api.ConfigDefaults.DEFAULT_CIVISIBILITY_ENABLED; +import static datadog.trace.api.ConfigDefaults.DEFAULT_CODE_ORIGIN_FOR_SPANS_ENABLED; import static datadog.trace.api.ConfigDefaults.DEFAULT_IAST_ENABLED; import static datadog.trace.api.ConfigDefaults.DEFAULT_INTEGRATIONS_ENABLED; import static datadog.trace.api.ConfigDefaults.DEFAULT_MEASURE_METHODS; @@ -16,8 +17,6 @@ import static datadog.trace.api.ConfigDefaults.DEFAULT_TRACE_EXECUTORS_ALL; import static datadog.trace.api.ConfigDefaults.DEFAULT_TRACE_METHODS; import static datadog.trace.api.ConfigDefaults.DEFAULT_TRACE_OTEL_ENABLED; -import static datadog.trace.api.ConfigDefaults.DEFAULT_TRACE_SPAN_ORIGIN_ENABLED; -import static datadog.trace.api.ConfigDefaults.DEFAULT_TRACE_SPAN_ORIGIN_ENRICHED; import static datadog.trace.api.ConfigDefaults.DEFAULT_USM_ENABLED; import static datadog.trace.api.config.AppSecConfig.APPSEC_ENABLED; import static datadog.trace.api.config.CiVisibilityConfig.CIVISIBILITY_ENABLED; @@ -32,6 +31,7 @@ import static datadog.trace.api.config.ProfilingConfig.PROFILING_ENABLED; import static datadog.trace.api.config.ProfilingConfig.PROFILING_ENABLED_DEFAULT; import static datadog.trace.api.config.TraceInstrumentationConfig.AXIS_TRANSPORT_CLASS_NAME; +import static datadog.trace.api.config.TraceInstrumentationConfig.CODE_ORIGIN_FOR_SPANS_ENABLED; import static datadog.trace.api.config.TraceInstrumentationConfig.EXPERIMENTAL_DEFER_INTEGRATIONS_UNTIL; import static datadog.trace.api.config.TraceInstrumentationConfig.HTTP_URL_CONNECTION_CLASS_NAME; import static datadog.trace.api.config.TraceInstrumentationConfig.INSTRUMENTATION_CONFIG_ID; @@ -63,8 +63,6 @@ import static datadog.trace.api.config.TraceInstrumentationConfig.TRACE_EXTENSIONS_PATH; import static datadog.trace.api.config.TraceInstrumentationConfig.TRACE_METHODS; import static datadog.trace.api.config.TraceInstrumentationConfig.TRACE_OTEL_ENABLED; -import static datadog.trace.api.config.TraceInstrumentationConfig.TRACE_SPAN_ORIGIN_ENABLED; -import static datadog.trace.api.config.TraceInstrumentationConfig.TRACE_SPAN_ORIGIN_ENRICHED; import static datadog.trace.api.config.TraceInstrumentationConfig.TRACE_THREAD_POOL_EXECUTORS_EXCLUDE; import static datadog.trace.api.config.UsmConfig.USM_ENABLED; import static datadog.trace.util.CollectionUtils.tryMakeImmutableList; @@ -102,8 +100,7 @@ public class InstrumenterConfig { private final boolean integrationsEnabled; - private final boolean spanOriginEnabled; - private final boolean spanOriginEnriched; + private final boolean codeOriginEnabled; private final boolean traceEnabled; private final boolean traceOtelEnabled; private final boolean logs128bTraceIdEnabled; @@ -176,10 +173,9 @@ private InstrumenterConfig() { integrationsEnabled = configProvider.getBoolean(INTEGRATIONS_ENABLED, DEFAULT_INTEGRATIONS_ENABLED); - spanOriginEnabled = - configProvider.getBoolean(TRACE_SPAN_ORIGIN_ENABLED, DEFAULT_TRACE_SPAN_ORIGIN_ENABLED); - spanOriginEnriched = - configProvider.getBoolean(TRACE_SPAN_ORIGIN_ENRICHED, DEFAULT_TRACE_SPAN_ORIGIN_ENRICHED); + codeOriginEnabled = + configProvider.getBoolean( + CODE_ORIGIN_FOR_SPANS_ENABLED, DEFAULT_CODE_ORIGIN_FOR_SPANS_ENABLED); traceEnabled = configProvider.getBoolean(TRACE_ENABLED, DEFAULT_TRACE_ENABLED); traceOtelEnabled = configProvider.getBoolean(TRACE_OTEL_ENABLED, DEFAULT_TRACE_OTEL_ENABLED); logs128bTraceIdEnabled = @@ -274,12 +270,8 @@ private InstrumenterConfig() { tryMakeImmutableSet(configProvider.getList(JAX_RS_ADDITIONAL_ANNOTATIONS)); } - public boolean isSpanOriginEnabled() { - return spanOriginEnabled; - } - - public boolean isSpanOriginEnriched() { - return spanOriginEnriched; + public boolean isCodeOriginEnabled() { + return codeOriginEnabled; } public boolean isTriageEnabled() { @@ -584,10 +576,8 @@ public String toString() { + runtimeContextFieldInjection + ", serialVersionUIDFieldInjection=" + serialVersionUIDFieldInjection - + ", spanOriginEnabled=" - + spanOriginEnabled - + ", spanOriginEnriched=" - + spanOriginEnriched + + ", codeOriginEnabled=" + + codeOriginEnabled + ", traceAnnotations='" + traceAnnotations + '\''