Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion dd-java-agent/agent-debugger/debugger-el/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ excludedClassesCoverage += [
'com.datadog.debugger.el.Script*',
'com.datadog.debugger.el.ValueScript*',
'com.datadog.debugger.el.values.CollectionValue*',
'com.datadog.debugger.el.InvalidValueException'
'com.datadog.debugger.el.InvalidValueException',
'com.datadog.debugger.el.EvaluationException'
]

dependencies {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.datadog.debugger.el;

import java.util.Objects;

public class EvaluationException extends RuntimeException {
private final String expr;

Expand All @@ -16,4 +18,22 @@ public EvaluationException(String message, String expr, Throwable cause) {
public String getExpr() {
return expr;
}

@Override
public final boolean equals(Object o) {
if (!(o instanceof EvaluationException)) {
return false;
}

EvaluationException that = (EvaluationException) o;
return Objects.equals(getExpr(), that.getExpr())
&& Objects.equals(getMessage(), that.getMessage());
}

@Override
public int hashCode() {
int result = Objects.hashCode(getExpr());
result = 31 * result + Objects.hashCode(getMessage());
return result;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -634,7 +634,10 @@ private List<ToInstrumentInfo> filterAndSortDefinitions(
// Log and span decoration probe shared the same instrumentor: CaptureContextInstrumentor
// and therefore need to be instrumented once
// note: exception probes are log probes and are handled the same way
if (isCapturedContextProbe(definition)) {
if (!Config.get().isDistributedDebuggerEnabled() && definition instanceof TriggerProbe) {
log.debug(
"The distributed debugger feature is disabled. Trigger probes will not be installed.");
} else if (isCapturedContextProbe(definition)) {
if (definition.isLineProbe()) {
capturedContextLineProbes
.computeIfAbsent(definition.getWhere(), key -> new ArrayList<>())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,6 @@ public InstrumentationResult.Status instrument(
.instrument();
}

public String getSessionId() {
return sessionId;
}

public TriggerProbe setSessionId(String sessionId) {
this.sessionId = sessionId;
return this;
Expand All @@ -86,6 +82,7 @@ public TriggerProbe setProbeCondition(ProbeCondition probeCondition) {
public void evaluate(
CapturedContext context, CapturedContext.Status status, MethodLocation location) {

Sampling sampling = getSampling();
if (sampling == null || !sampling.inCoolDown()) {
boolean sample = true;
if (!hasCondition()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,14 @@
import static java.lang.String.format;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
import static utils.InstrumentationTestHelper.compileAndLoadClass;

import com.datadog.debugger.agent.CapturingTestBase;
import com.datadog.debugger.agent.Configuration;
import com.datadog.debugger.agent.MockSampler;
import com.datadog.debugger.el.EvaluationException;
import com.datadog.debugger.el.ProbeCondition;
import com.datadog.debugger.probe.Sampling;
import com.datadog.debugger.probe.TriggerProbe;
Expand Down Expand Up @@ -50,6 +53,53 @@ public void before() {

setFieldInConfig(Config.get(), "debuggerCodeOriginEnabled", true);
setFieldInConfig(InstrumenterConfig.get(), "codeOriginEnabled", true);
setFieldInConfig(Config.get(), "distributedDebuggerEnabled", true);
}

@Test
public void conditions() throws IOException, URISyntaxException {
final String className = "com.datadog.debugger.TriggerProbe02";
TriggerProbe probe1 =
createTriggerProbe(
TRIGGER_PROBE_ID1,
TRIGGER_PROBE_SESSION_ID,
className,
"entry",
"(int)",
new ProbeCondition(when(lt(ref("value"), value(25))), "value < 25"),
new Sampling(10.0));
installProbes(Configuration.builder().setService(SERVICE_NAME).add(probe1).build());
Class<?> testClass = compileAndLoadClass(className);
for (int i = 0; i < 100; i++) {
Reflect.onClass(testClass).call("main", i).get();
}
List<List<? extends MutableSpan>> allTraces = traceInterceptor.getAllTraces();
long count =
allTraces.stream()
.map(span -> span.get(0))
.filter(
span -> {
DDSpan ddSpan = (DDSpan) span;
PropagationTags tags = ddSpan.context().getPropagationTags();
return (TRIGGER_PROBE_SESSION_ID + ":1").equals(tags.getDebugPropagation());
})
.count();
assertEquals(100, allTraces.size(), "actual traces: " + allTraces.size());
assertTrue(count <= 25, "Should have at most 25 debug sessions. found: " + count);
}

private static TriggerProbe createTriggerProbe(
ProbeId id,
String sessionId,
String typeName,
String methodName,
String signature,
ProbeCondition probeCondition,
Sampling sampling) {
return new TriggerProbe(id, Where.of(typeName, methodName, signature))
.setSessionId(sessionId)
.setProbeCondition(probeCondition)
.setSampling(sampling);
}

@Test
Expand Down Expand Up @@ -104,6 +154,58 @@ public void cooldown() throws IOException, URISyntaxException {
}
}

@Test
public void badCondition() throws IOException, URISyntaxException {
String className = "com.datadog.debugger.TriggerProbe02";
TriggerProbe probe1 =
createTriggerProbe(
TRIGGER_PROBE_ID1,
TRIGGER_PROBE_SESSION_ID,
className,
"entry",
"(int)",
new ProbeCondition(when(lt(ref("limit"), value(25))), "limit < 25"),
new Sampling(10.0));

installProbes(Configuration.builder().setService(SERVICE_NAME).add(probe1).build());
Class<?> testClass = compileAndLoadClass(className);
Reflect.onClass(testClass).call("main", 0).get();
verify(probeStatusSink)
.addError(
eq(TRIGGER_PROBE_ID1),
eq(new EvaluationException("Cannot find symbol: limit", "limit")));
}

@Test
public void debuggerDisabled() throws IOException, URISyntaxException {
boolean original = Config.get().isDistributedDebuggerEnabled();
try {
setFieldInConfig(Config.get(), "distributedDebuggerEnabled", false);

MockSampler sampler = new MockSampler();
ProbeRateLimiter.setSamplerSupplier(value -> sampler);

final String className = "com.datadog.debugger.TriggerProbe02";
TriggerProbe probe1 =
createTriggerProbe(
TRIGGER_PROBE_ID1,
TRIGGER_PROBE_SESSION_ID,
className,
"entry",
"(int)",
new ProbeCondition(when(lt(ref("value"), value(25))), "value < 25"),
new Sampling(10.0));
installProbes(Configuration.builder().setService(SERVICE_NAME).add(probe1).build());
Class<?> testClass = compileAndLoadClass(className);
Reflect.onClass(testClass).call("main", 0).get();

assertEquals(0, sampler.getCallCount());
} finally {
setFieldInConfig(Config.get(), "distributedDebuggerEnabled", original);
ProbeRateLimiter.setSamplerSupplier(null);
}
}

@Test
public void sampling() throws IOException, URISyntaxException {
try {
Expand Down Expand Up @@ -134,49 +236,25 @@ public void sampling() throws IOException, URISyntaxException {
}

@Test
public void conditions() throws IOException, URISyntaxException {
public void noSampling() throws IOException, URISyntaxException {
try {
MockSampler sampler = new MockSampler();
ProbeRateLimiter.setSamplerSupplier(value -> sampler);

final String className = "com.datadog.debugger.TriggerProbe02";
TriggerProbe probe1 =
createTriggerProbe(
TRIGGER_PROBE_ID1,
TRIGGER_PROBE_SESSION_ID,
className,
"entry",
"(int)",
new ProbeCondition(when(lt(ref("value"), value(25))), "value < 25"),
new Sampling(10.0));
installProbes(Configuration.builder().setService(SERVICE_NAME).add(probe1).build());
Class<?> testClass = compileAndLoadClass(className);
for (int i = 0; i < 100; i++) {
Reflect.onClass(testClass).call("main", i).get();
}
List<List<? extends MutableSpan>> allTraces = traceInterceptor.getAllTraces();
long count =
allTraces.stream()
.map(span -> span.get(0))
.filter(
span -> {
DDSpan ddSpan = (DDSpan) span;
PropagationTags tags = ddSpan.context().getPropagationTags();
return (TRIGGER_PROBE_SESSION_ID + ":1").equals(tags.getDebugPropagation());
})
.count();
assertEquals(100, allTraces.size(), "actual traces: " + allTraces.size());
assertTrue(count <= 25, "Should have at most 25 debug sessions. found: " + count);
}
final String className = "com.datadog.debugger.TriggerProbe01";
TriggerProbe probe1 =
createTriggerProbe(
TRIGGER_PROBE_ID1, TRIGGER_PROBE_SESSION_ID, className, "entry", "()", null, null);
Configuration config = Configuration.builder().setService(SERVICE_NAME).add(probe1).build();
installProbes(config);
Class<?> testClass = compileAndLoadClass(className);
for (int i = 0; i < 100; i++) {
Reflect.onClass(testClass).call("main", "").get();
}

public static TriggerProbe createTriggerProbe(
ProbeId id,
String sessionId,
String typeName,
String methodName,
String signature,
ProbeCondition probeCondition,
Sampling sampling) {
return new TriggerProbe(id, Where.of(typeName, methodName, signature))
.setSessionId(sessionId)
.setProbeCondition(probeCondition)
.setSampling(sampling);
assertTrue(sampler.getCallCount() != 0);
} finally {
ProbeRateLimiter.setSamplerSupplier(null);
}
}
}
Loading