Skip to content

Commit

Permalink
Introduce new flag to enable the live debugger specifically (#8418)
Browse files Browse the repository at this point in the history
  • Loading branch information
evanchooly authored Feb 21, 2025
1 parent bde8272 commit 19ce7f1
Show file tree
Hide file tree
Showing 7 changed files with 70 additions and 46 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ private CodeOriginProbe createProbe(

// i think this check is unnecessary at this point time but leaving for now to be safe
if (installed == null) {
if (Config.get().isDynamicInstrumentationEnabled()) {
if (Config.get().isDistributedDebuggerEnabled()) {
registerLogProbe(probe);
}
installProbes();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ public void before() {
tracer.addTraceInterceptor(traceInterceptor);

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import datadog.trace.bootstrap.debugger.EvaluationError;
import datadog.trace.bootstrap.debugger.MethodLocation;
import datadog.trace.bootstrap.debugger.ProbeId;
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;
Expand All @@ -30,6 +31,7 @@
import datadog.trace.bootstrap.instrumentation.api.Tags;
import datadog.trace.core.CoreTracer;
import java.util.stream.Stream;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
Expand All @@ -41,6 +43,7 @@ public class LogProbeTest {
private static final String LANGUAGE = "java";
private static final ProbeId PROBE_ID = new ProbeId("beae1807-f3b0-4ea8-a74f-826790c5e6f8", 0);
private static final String DEBUG_SESSION_ID = "TestSession";
private static final int BUDGET_RUNS = 1100;

@Test
public void testCapture() {
Expand Down Expand Up @@ -81,65 +84,68 @@ public void noDebugSession() {

@Test
public void budgets() {
BudgetSink sink = new BudgetSink(getConfig(), mock(ProbeStatusSink.class));
DebuggerAgentHelper.injectSink(sink);
try {
ProbeRateLimiter.setGlobalSnapshotRate(-1);
TracerAPI tracer =
CoreTracer.builder()
.idGenerationStrategy(IdGenerationStrategy.fromName("random"))
.build();
AgentTracer.registerIfAbsent(tracer);
String sessionId = "12345";

TracerAPI tracer =
CoreTracer.builder().idGenerationStrategy(IdGenerationStrategy.fromName("random")).build();
AgentTracer.registerIfAbsent(tracer);
int runs = 100;
for (int i = 0; i < runs; i++) {
runTrace(tracer, true);
}
assertEquals(runs * LogProbe.CAPTURING_PROBE_BUDGET, sink.captures);
Result result = getResult(tracer, sessionId, true, null);
assertEquals(BUDGET_RUNS * LogProbe.CAPTURING_PROBE_BUDGET, result.sink.captures);

sink = new BudgetSink(getConfig(), mock(ProbeStatusSink.class));
DebuggerAgentHelper.injectSink(sink);
runs = 1010;
for (int i = 0; i < runs; i++) {
runTrace(tracer, false);
}
assertEquals(runs * LogProbe.NON_CAPTURING_PROBE_BUDGET, sink.highRate);
}
result = getResult(tracer, sessionId, false, null);
assertEquals(BUDGET_RUNS * LogProbe.NON_CAPTURING_PROBE_BUDGET, result.sink.highRate);

@Test
public void budgetsOnLineProbes() {
BudgetSink sink = new BudgetSink(getConfig(), mock(ProbeStatusSink.class));
DebuggerAgentHelper.injectSink(sink);
// run without a session
result = getResult(tracer, null, true, 100);
assertEquals(result.count, result.sink.captures);

TracerAPI tracer =
CoreTracer.builder().idGenerationStrategy(IdGenerationStrategy.fromName("random")).build();
AgentTracer.registerIfAbsent(tracer);
int runs = 100;
for (int i = 0; i < runs; i++) {
runTrace(tracer, true, 100);
result = getResult(tracer, null, false, 100);
assertEquals(result.count, result.sink.highRate);
} finally {
ProbeRateLimiter.resetGlobalRate();
}
assertEquals(runs * LogProbe.CAPTURING_PROBE_BUDGET, sink.captures);
}

sink = new BudgetSink(getConfig(), mock(ProbeStatusSink.class));
@NotNull
private Result getResult(
TracerAPI tracer, String sessionId, boolean captureSnapshot, Integer line) {
BudgetSink sink = new BudgetSink(getConfig(), mock(ProbeStatusSink.class));
DebuggerAgentHelper.injectSink(sink);
runs = 1010;
for (int i = 0; i < runs; i++) {
runTrace(tracer, false, 100);
int count = 0;
for (int i = 0; i < BUDGET_RUNS; i++) {
count += runTrace(tracer, captureSnapshot, line, sessionId);
}
assertEquals(runs * LogProbe.NON_CAPTURING_PROBE_BUDGET, sink.highRate);
return new Result(sink, count);
}

private void runTrace(TracerAPI tracer, boolean captureSnapshot) {
runTrace(tracer, captureSnapshot, null);
private static class Result {
final int count;
final BudgetSink sink;

private Result(BudgetSink sink, int count) {
this.sink = sink;
this.count = count;
}
}

private void runTrace(TracerAPI tracer, boolean captureSnapshot, Integer line) {
private int runTrace(TracerAPI tracer, boolean captureSnapshot, Integer line, String sessionId) {
AgentSpan span = tracer.startSpan("budget testing", "test span");
span.setTag(Tags.PROPAGATED_DEBUG, "12345:1");
if (sessionId != null) {
span.setTag(Tags.PROPAGATED_DEBUG, sessionId + ":1");
}
try (AgentScope scope = tracer.activateSpan(span, ScopeSource.MANUAL)) {
Builder builder =
createLog("Budget testing").probeId(ProbeId.newId()).captureSnapshot(captureSnapshot);
if (sessionId != null) {
builder.tags("session_id:" + sessionId);
}
LogProbe logProbe = builder.build();
ProbeRateLimiter.setRate(logProbe.id, -1, captureSnapshot);

LogProbe logProbe =
createLog("I'm in a debug session")
.probeId(ProbeId.newId())
.tags("session_id:12345")
.captureSnapshot(captureSnapshot)
.build();
CapturedContext entryContext = capturedContext(span, logProbe);
CapturedContext exitContext = capturedContext(span, logProbe);
logProbe.evaluate(entryContext, new LogStatus(logProbe), MethodLocation.ENTRY);
Expand All @@ -158,7 +164,11 @@ private void runTrace(TracerAPI tracer, boolean captureSnapshot, Integer line) {
logProbe.commit(entryContext, line);
}
}
assertEquals(runs, span.getLocalRootSpan().getTag(format("_dd.ld.probe_id.%s", logProbe.id)));
if (sessionId != null) {
assertEquals(
runs, span.getLocalRootSpan().getTag(format("_dd.ld.probe_id.%s", logProbe.id)));
}
return runs;
}
}

Expand Down Expand Up @@ -343,6 +353,7 @@ private Builder createLog(String template) {
private static class BudgetSink extends DebuggerSink {

public int captures;

public int highRate;

public BudgetSink(Config config, ProbeStatusSink probeStatusSink) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ public final class ConfigDefaults {
static final boolean DEFAULT_DEBUGGER_EXCEPTION_CAPTURE_INTERMEDIATE_SPANS_ENABLED = true;
static final int DEFAULT_DEBUGGER_EXCEPTION_MAX_CAPTURED_FRAMES = 3;
static final int DEFAULT_DEBUGGER_EXCEPTION_CAPTURE_INTERVAL_SECONDS = 60 * 60;
static final boolean DEFAULT_DISTRIBUTED_DEBUGGER_ENABLED = false;

static final boolean DEFAULT_TRACE_REPORT_HOSTNAME = false;
static final String DEFAULT_TRACE_ANNOTATIONS = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ public final class DebuggerConfig {
"exception.replay.capture.interval.seconds";
public static final String DEBUGGER_EXCEPTION_CAPTURE_INTERMEDIATE_SPANS_ENABLED =
"exception.replay.capture.intermediate.spans.enabled";
public static final String DISTRIBUTED_DEBUGGER_ENABLED = "distributed.debugger.enabled";
public static final String THIRD_PARTY_INCLUDES = "third.party.includes";
public static final String THIRD_PARTY_EXCLUDES = "third.party.excludes";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,8 @@ public void toJson(JsonWriter writer, Config config) throws IOException {
writer.value(config.isDebuggerExceptionEnabled());
writer.name("debugger_span_origin_enabled");
writer.value(config.isDebuggerCodeOriginEnabled());
writer.name("debugger_distributed_debugger_enabled");
writer.value(config.isDistributedDebuggerEnabled());
writer.name("appsec_enabled");
writer.value(config.getAppSecActivation().toString());
writer.name("appsec_rules_file_path");
Expand Down
8 changes: 8 additions & 0 deletions internal-api/src/main/java/datadog/trace/api/Config.java
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,7 @@ public static String getHostName() {
private final int debuggerExceptionCaptureInterval;
private final boolean debuggerCodeOriginEnabled;
private final int debuggerCodeOriginMaxUserFrames;
private final boolean distributedDebuggerEnabled;

private final Set<String> debuggerThirdPartyIncludes;
private final Set<String> debuggerThirdPartyExcludes;
Expand Down Expand Up @@ -1559,6 +1560,9 @@ PROFILING_DATADOG_PROFILER_ENABLED, isDatadogProfilerSafeInCurrentEnvironment())
dynamicInstrumentationEnabled =
configProvider.getBoolean(
DYNAMIC_INSTRUMENTATION_ENABLED, DEFAULT_DYNAMIC_INSTRUMENTATION_ENABLED);
distributedDebuggerEnabled =
configProvider.getBoolean(
DISTRIBUTED_DEBUGGER_ENABLED, DEFAULT_DISTRIBUTED_DEBUGGER_ENABLED);
dynamicInstrumentationUploadTimeout =
configProvider.getInteger(
DYNAMIC_INSTRUMENTATION_UPLOAD_TIMEOUT, DEFAULT_DYNAMIC_INSTRUMENTATION_UPLOAD_TIMEOUT);
Expand Down Expand Up @@ -3171,6 +3175,10 @@ public int getDebuggerCodeOriginMaxUserFrames() {
return debuggerCodeOriginMaxUserFrames;
}

public boolean isDistributedDebuggerEnabled() {
return distributedDebuggerEnabled;
}

public Set<String> getThirdPartyIncludes() {
return debuggerThirdPartyIncludes;
}
Expand Down

0 comments on commit 19ce7f1

Please sign in to comment.