Skip to content

Commit c08dc4f

Browse files
authored
Merge pull request #7286 from DataDog/evanchooly/DEBUG-2434
Implement debug context propagation to enable live debugging of java applications
2 parents 42654e4 + c126dd8 commit c08dc4f

File tree

44 files changed

+840
-205
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+840
-205
lines changed

dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/span_origin/EntrySpanOriginInfo.java

Lines changed: 0 additions & 36 deletions
This file was deleted.

dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/span_origin/ExitSpanOriginInfo.java

Lines changed: 0 additions & 54 deletions
This file was deleted.

dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/span_origin/FindFirstStackTraceElement.java

Lines changed: 0 additions & 25 deletions
This file was deleted.

dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/span_origin/LineInfo.java

Lines changed: 0 additions & 31 deletions
This file was deleted.

dd-java-agent/agent-debugger/debugger-bootstrap/src/main/java/datadog/trace/bootstrap/debugger/DebuggerContext.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,12 +67,17 @@ public interface ExceptionDebugger {
6767
void handleException(Throwable t, AgentSpan span);
6868
}
6969

70+
public interface SpanDebugger {
71+
String captureSnapshot(String signature);
72+
}
73+
7074
private static volatile ProbeResolver probeResolver;
7175
private static volatile ClassFilter classFilter;
7276
private static volatile MetricForwarder metricForwarder;
7377
private static volatile Tracer tracer;
7478
private static volatile ValueSerializer valueSerializer;
7579
private static volatile ExceptionDebugger exceptionDebugger;
80+
private static volatile SpanDebugger spanDebugger;
7681

7782
public static void initProbeResolver(ProbeResolver probeResolver) {
7883
DebuggerContext.probeResolver = probeResolver;
@@ -98,6 +103,10 @@ public static void initExceptionDebugger(ExceptionDebugger exceptionDebugger) {
98103
DebuggerContext.exceptionDebugger = exceptionDebugger;
99104
}
100105

106+
public static void initSpanDebugger(SpanDebugger spanDebugger) {
107+
DebuggerContext.spanDebugger = spanDebugger;
108+
}
109+
101110
/**
102111
* Returns the probe details based on the probe id provided. If no probe is found, try to
103112
* re-transform the class using the callingClass parameter No-op if no implementation available
@@ -333,6 +342,18 @@ public static void commit(
333342
}
334343
}
335344

345+
public static String captureSnapshot(String signature) {
346+
try {
347+
SpanDebugger debugger = spanDebugger;
348+
if (debugger != null) {
349+
return debugger.captureSnapshot(signature);
350+
}
351+
} catch (Exception ex) {
352+
LOGGER.debug("Error in addSnapshot: ", ex);
353+
}
354+
return null;
355+
}
356+
336357
public static void handleException(Throwable t, AgentSpan span) {
337358
try {
338359
ExceptionDebugger exDebugger = exceptionDebugger;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package datadog.trace.bootstrap.debugger.spanorigin;
2+
3+
import static datadog.trace.bootstrap.debugger.DebuggerContext.captureSnapshot;
4+
import static java.util.Arrays.stream;
5+
6+
import datadog.trace.api.InstrumenterConfig;
7+
import datadog.trace.bootstrap.instrumentation.api.AgentSpan;
8+
import java.lang.reflect.Method;
9+
import java.util.stream.Collectors;
10+
11+
public class SpanOriginInfo {
12+
public static void entry(AgentSpan span, Method method) {
13+
if (InstrumenterConfig.get().isSpanOriginEnabled()) {
14+
String signature =
15+
stream(method.getParameterTypes())
16+
.map(Class::getName)
17+
.collect(Collectors.joining(",", "(", ")"));
18+
captureSnapshot(signature);
19+
}
20+
}
21+
22+
public static void exit(AgentSpan span) {
23+
if (InstrumenterConfig.get().isSpanOriginEnabled()) {
24+
span.getLocalRootSpan().setMetaStruct(captureSnapshot(null), span);
25+
}
26+
}
27+
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
public interface ConfigurationAcceptor {
77
enum Source {
88
REMOTE_CONFIG,
9+
SPAN_DEBUG,
910
EXCEPTION
1011
}
1112

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import com.datadog.debugger.sink.ProbeStatusSink;
1010
import com.datadog.debugger.sink.SnapshotSink;
1111
import com.datadog.debugger.sink.SymbolSink;
12+
import com.datadog.debugger.snapshot.DefaultSpanDebugger;
1213
import com.datadog.debugger.symbol.SymDBEnablement;
1314
import com.datadog.debugger.symbol.SymbolAggregator;
1415
import com.datadog.debugger.uploader.BatchUploader;
@@ -98,6 +99,10 @@ public static synchronized void run(
9899
config.getDebuggerMaxExceptionPerSecond());
99100
DebuggerContext.initExceptionDebugger(defaultExceptionDebugger);
100101
}
102+
if (config.isDebuggerSpanDebugEnabled()) {
103+
DebuggerContext.initSpanDebugger(
104+
new DefaultSpanDebugger(configurationUpdater, classNameFiltering));
105+
}
101106
if (config.isDebuggerInstrumentTheWorld()) {
102107
setupInstrumentTheWorldTransformer(
103108
config, instrumentation, debuggerSink, statsdMetricForwarder);

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import com.datadog.debugger.instrumentation.InstrumentationResult;
88
import com.datadog.debugger.instrumentation.MethodInfo;
99
import com.datadog.debugger.probe.ExceptionProbe;
10+
import com.datadog.debugger.probe.ForceMethodInstrumentation;
1011
import com.datadog.debugger.probe.LogProbe;
1112
import com.datadog.debugger.probe.MetricProbe;
1213
import com.datadog.debugger.probe.ProbeDefinition;
@@ -609,7 +610,7 @@ private ProbeDefinition selectReferenceDefinition(
609610
ProbeId probeId = capturedContextProbes.get(0).getProbeId();
610611
for (ProbeDefinition definition : capturedContextProbes) {
611612
if (definition instanceof LogProbe) {
612-
if (definition instanceof ExceptionProbe) {
613+
if (definition instanceof ForceMethodInstrumentation) {
613614
where = Where.convertLineToMethod(where, classFileLines);
614615
}
615616
hasLogProbe = true;

dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/exception/DefaultExceptionDebugger.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ public void handleException(Throwable t, AgentSpan span) {
7979
return;
8080
}
8181
if (exceptionProbeManager.isAlreadyInstrumented(fingerprint)) {
82-
ThrowableState state = exceptionProbeManager.getSateByThrowable(innerMostException);
82+
ThrowableState state = exceptionProbeManager.getStateByThrowable(innerMostException);
8383
if (state == null) {
8484
LOGGER.debug("Unable to find state for throwable: {}", innerMostException.toString());
8585
return;

dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/exception/ExceptionProbeManager.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ public void addSnapshot(Snapshot snapshot) {
147147
state.addSnapshot(snapshot);
148148
}
149149

150-
public ThrowableState getSateByThrowable(Throwable throwable) {
150+
public ThrowableState getStateByThrowable(Throwable throwable) {
151151
return snapshotsByThrowable.get(throwable);
152152
}
153153

dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/exception/Fingerprinter.java

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,30 +4,33 @@
44

55
import com.datadog.debugger.util.ClassNameFiltering;
66
import java.security.MessageDigest;
7-
import java.security.NoSuchAlgorithmException;
87
import org.slf4j.Logger;
98
import org.slf4j.LoggerFactory;
109

1110
/** Computes a fingerprint of an exception based on its stacktrace and exception type. */
1211
public class Fingerprinter {
1312
private static final Logger LOGGER = LoggerFactory.getLogger(Fingerprinter.class);
13+
private static MessageDigest digest;
14+
15+
static {
16+
try {
17+
digest = MessageDigest.getInstance("SHA-256");
18+
} catch (Throwable e) {
19+
LOGGER.debug("Unable to find digest algorithm SHA-256", e);
20+
}
21+
}
1422

1523
// compute fingerprint of the Throwable based on the stacktrace and exception type
1624
public static String fingerprint(Throwable t, ClassNameFiltering classNameFiltering) {
25+
if (digest == null) {
26+
return null;
27+
}
1728
t = getInnerMostThrowable(t);
1829
if (t == null) {
1930
LOGGER.debug("Unable to find root cause of exception");
2031
return null;
2132
}
2233
Class<? extends Throwable> clazz = t.getClass();
23-
MessageDigest digest;
24-
try {
25-
// TODO avoid lookup a new instance every time
26-
digest = MessageDigest.getInstance("SHA-256");
27-
} catch (NoSuchAlgorithmException e) {
28-
LOGGER.debug("Unable to find digest algorithm SHA-256", e);
29-
return null;
30-
}
3134
String typeName = clazz.getTypeName();
3235
digest.update(typeName.getBytes());
3336
StackTraceElement[] stackTrace = t.getStackTrace();
@@ -38,8 +41,15 @@ public static String fingerprint(Throwable t, ClassNameFiltering classNameFilter
3841
}
3942
digest.update(stackTraceElement.toString().getBytes());
4043
}
41-
byte[] bytes = digest.digest();
42-
return bytesToHex(bytes);
44+
return bytesToHex(digest.digest());
45+
}
46+
47+
public static String fingerprint(StackTraceElement element) {
48+
if (digest == null) {
49+
return null;
50+
}
51+
digest.update(element.toString().getBytes());
52+
return bytesToHex(digest.digest());
4353
}
4454

4555
// convert byte[] to hex string

dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/ExceptionProbe.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
import org.slf4j.Logger;
1818
import org.slf4j.LoggerFactory;
1919

20-
public class ExceptionProbe extends LogProbe {
20+
public class ExceptionProbe extends LogProbe implements ForceMethodInstrumentation {
2121
private static final Logger LOGGER = LoggerFactory.getLogger(ExceptionProbe.class);
2222
private final transient ExceptionProbeManager exceptionProbeManager;
2323

@@ -76,7 +76,7 @@ public void evaluate(
7676
// capture only on uncaught exception matching the fingerprint
7777
ExceptionProbeStatus exceptionStatus = (ExceptionProbeStatus) status;
7878
ExceptionProbeManager.ThrowableState state =
79-
exceptionProbeManager.getSateByThrowable(innerMostThrowable);
79+
exceptionProbeManager.getStateByThrowable(innerMostThrowable);
8080
if (state != null) {
8181
// Already unwinding the exception
8282
if (!state.isSampling()) {
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package com.datadog.debugger.probe;
2+
3+
/**
4+
* This marker interface indicates that, regardless of the {@link Where} field, this probe should
5+
* always be treated as a method probe for purposes of instrumentation and evaluation. In the cases
6+
* of {@link ExceptionProbe} and {@link SpanDebuggerProbe}, e.g., these probes need to be
7+
* instrumented as method probes even though specific lines are listed in their {@link Where} fields
8+
* because the information they are collecting is very much related to specific lines but the
9+
* lifecycle of a line probe is insufficient to gather that data.
10+
*/
11+
public interface ForceMethodInstrumentation {}

dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/LogProbe.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -584,9 +584,9 @@ public CapturedContext.Status createStatus() {
584584
}
585585

586586
public static class LogStatus extends CapturedContext.Status {
587-
private static final LogStatus EMPTY_LOG_STATUS =
587+
public static final LogStatus EMPTY_LOG_STATUS =
588588
new LogStatus(ProbeImplementation.UNKNOWN, false);
589-
private static final LogStatus EMPTY_CAPTURING_LOG_STATUS =
589+
public static final LogStatus EMPTY_CAPTURING_LOG_STATUS =
590590
new LogStatus(ProbeImplementation.UNKNOWN, true);
591591

592592
private boolean condition = true;

0 commit comments

Comments
 (0)