From d96ebf44f3422b7888928371bba8ce8ee6c503eb Mon Sep 17 00:00:00 2001 From: jean-philippe bempel Date: Tue, 21 Oct 2025 10:34:08 +0200 Subject: [PATCH] optimize method probe without condition In case of a method probe without condition we can move the sampling is currently done in LogProbe::evaluate to LogProbe::isReadToCapture. This way if the sample fails we return false on isReadyTocapture and no CapturedContext is created and nothing captured. If we sample the execution the CapturedContext will be created we capture the context and serialize the result, but we are sure that we are not doing for nothing. We introduce also a differentiation for single probe/multiple probe in calling evaluate that is propagated to probe implementation. it helps to differentiate the behavior based on the instrumentation --- .../bootstrap/debugger/CapturedContext.java | 5 ++-- .../bootstrap/debugger/DebuggerContext.java | 17 ++++++++++--- .../debugger/ProbeImplementation.java | 10 ++++++-- .../debugger/probe/ExceptionProbe.java | 7 ++++-- .../com/datadog/debugger/probe/LogProbe.java | 25 +++++++++++-------- .../debugger/probe/ProbeDefinition.java | 5 +++- .../debugger/probe/SpanDecorationProbe.java | 5 +++- .../datadog/debugger/probe/TriggerProbe.java | 5 +++- .../agent/SnapshotSerializationTest.java | 3 ++- .../DefaultExceptionDebuggerTest.java | 3 ++- .../datadog/debugger/probe/LogProbeTest.java | 16 +++++++----- 11 files changed, 70 insertions(+), 31 deletions(-) diff --git a/dd-java-agent/agent-debugger/debugger-bootstrap/src/main/java/datadog/trace/bootstrap/debugger/CapturedContext.java b/dd-java-agent/agent-debugger/debugger-bootstrap/src/main/java/datadog/trace/bootstrap/debugger/CapturedContext.java index 7329b3f1fbf..1cd4339982c 100644 --- a/dd-java-agent/agent-debugger/debugger-bootstrap/src/main/java/datadog/trace/bootstrap/debugger/CapturedContext.java +++ b/dd-java-agent/agent-debugger/debugger-bootstrap/src/main/java/datadog/trace/bootstrap/debugger/CapturedContext.java @@ -297,7 +297,8 @@ public Status evaluate( ProbeImplementation probeImplementation, String thisClassName, long startTimestamp, - MethodLocation methodLocation) { + MethodLocation methodLocation, + boolean singleProbe) { Status status = statusByProbeId.computeIfAbsent( probeImplementation.getProbeId().getEncodedId(), @@ -311,7 +312,7 @@ public Status evaluate( boolean shouldEvaluate = MethodLocation.isSame(methodLocation, probeImplementation.getEvaluateAt()); if (shouldEvaluate) { - probeImplementation.evaluate(this, status, methodLocation); + probeImplementation.evaluate(this, status, methodLocation, singleProbe); } return status; } 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 b22de7dadd0..36bd7dd6f86 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 @@ -311,7 +311,11 @@ public static void evalContext( } CapturedContext.Status status = context.evaluate( - probeImplementation, callingClass.getTypeName(), startTimestamp, methodLocation); + probeImplementation, + callingClass.getTypeName(), + startTimestamp, + methodLocation, + false); needFreeze |= status.shouldFreezeContext(); } // only freeze the context when we have at lest one snapshot probe, and we should send @@ -343,7 +347,11 @@ public static void evalContext( } CapturedContext.Status status = context.evaluate( - probeImplementation, callingClass.getTypeName(), startTimestamp, methodLocation); + probeImplementation, + callingClass.getTypeName(), + startTimestamp, + methodLocation, + true); boolean needFreeze = status.shouldFreezeContext(); // only freeze the context when we have at lest one snapshot probe, and we should send // snapshot @@ -371,7 +379,7 @@ public static void evalContextAndCommit( continue; } context.evaluate( - probeImplementation, callingClass.getTypeName(), -1, MethodLocation.DEFAULT); + probeImplementation, callingClass.getTypeName(), -1, MethodLocation.DEFAULT, false); probeImplementations.add(probeImplementation); } for (ProbeImplementation probeImplementation : probeImplementations) { @@ -395,7 +403,8 @@ public static void evalContextAndCommit( if (probeImplementation == null) { return; } - context.evaluate(probeImplementation, callingClass.getTypeName(), -1, MethodLocation.DEFAULT); + context.evaluate( + probeImplementation, callingClass.getTypeName(), -1, MethodLocation.DEFAULT, true); probeImplementation.commit(context, line); } catch (Exception ex) { LOGGER.debug("Error in evalContextAndCommit: ", ex); diff --git a/dd-java-agent/agent-debugger/debugger-bootstrap/src/main/java/datadog/trace/bootstrap/debugger/ProbeImplementation.java b/dd-java-agent/agent-debugger/debugger-bootstrap/src/main/java/datadog/trace/bootstrap/debugger/ProbeImplementation.java index f5adfc0b5fc..819efe45da9 100644 --- a/dd-java-agent/agent-debugger/debugger-bootstrap/src/main/java/datadog/trace/bootstrap/debugger/ProbeImplementation.java +++ b/dd-java-agent/agent-debugger/debugger-bootstrap/src/main/java/datadog/trace/bootstrap/debugger/ProbeImplementation.java @@ -17,7 +17,10 @@ public interface ProbeImplementation { String getStrTags(); void evaluate( - CapturedContext context, CapturedContext.Status status, MethodLocation methodLocation); + CapturedContext context, + CapturedContext.Status status, + MethodLocation methodLocation, + boolean singleProbe); void commit( CapturedContext entryContext, @@ -82,7 +85,10 @@ public String getStrTags() { @Override public void evaluate( - CapturedContext context, CapturedContext.Status status, MethodLocation methodLocation) {} + CapturedContext context, + CapturedContext.Status status, + MethodLocation methodLocation, + boolean singleProbe) {} @Override public void commit( diff --git a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/ExceptionProbe.java b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/ExceptionProbe.java index 7daca19abae..7be31c4eb2a 100644 --- a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/ExceptionProbe.java +++ b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/ExceptionProbe.java @@ -71,7 +71,10 @@ public CapturedContext.Status createStatus() { @Override public void evaluate( - CapturedContext context, CapturedContext.Status status, MethodLocation methodLocation) { + CapturedContext context, + CapturedContext.Status status, + MethodLocation methodLocation, + boolean singleProbe) { ExceptionProbeStatus exceptionStatus; if (status instanceof ExceptionProbeStatus) { exceptionStatus = (ExceptionProbeStatus) status; @@ -108,7 +111,7 @@ public void evaluate( exceptionStatus.setForceSampling(true); } exceptionStatus.setCapture(true); - super.evaluate(context, status, methodLocation); + super.evaluate(context, status, methodLocation, singleProbe); } } diff --git a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/LogProbe.java b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/LogProbe.java index b81463a29de..8246e512b9d 100644 --- a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/LogProbe.java +++ b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/LogProbe.java @@ -485,7 +485,7 @@ public InstrumentationResult.Status instrument( @Override public boolean isReadyToCapture() { - if (isLineProbe() && !hasCondition()) { + if (!hasCondition()) { // we are sampling here to avoid creating CapturedContext when the sampling result is negative return ProbeRateLimiter.tryProbe(id); } @@ -494,20 +494,25 @@ public boolean isReadyToCapture() { @Override public void evaluate( - CapturedContext context, CapturedContext.Status status, MethodLocation methodLocation) { + CapturedContext context, + CapturedContext.Status status, + MethodLocation methodLocation, + boolean singleProbe) { if (!(status instanceof LogStatus)) { throw new IllegalStateException("Invalid status: " + status.getClass()); } LogStatus logStatus = (LogStatus) status; - if (isLineProbe() && !hasCondition()) { - // sampling was already done in isReadToCapture so we assume that if we are executing the - // current method it means the status should be sampled - if (!logStatus.getDebugSessionStatus().isDisabled()) { - logStatus.setSampled(true); + if (!hasCondition()) { + if (singleProbe) { + // sampling was already done in isReadToCapture so we assume that if we are executing the + // current method it means the status should be sampled + if (!logStatus.getDebugSessionStatus().isDisabled()) { + logStatus.setSampled(true); + } + } else { + // sample when no condition associated + sample(logStatus, methodLocation); } - } else if (!hasCondition()) { - // sample when no condition associated - sample(logStatus, methodLocation); } logStatus.setCondition(evaluateCondition(context, logStatus)); CapturedContext.CapturedThrowable throwable = context.getCapturedThrowable(); diff --git a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/ProbeDefinition.java b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/ProbeDefinition.java index 9cfa0602945..707ea9ee0de 100644 --- a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/ProbeDefinition.java +++ b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/ProbeDefinition.java @@ -137,7 +137,10 @@ public ProbeLocation getLocation() { @Override public void evaluate( - CapturedContext context, CapturedContext.Status status, MethodLocation methodLocation) {} + CapturedContext context, + CapturedContext.Status status, + MethodLocation methodLocation, + boolean singleProbe) {} @Override public void commit( diff --git a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/SpanDecorationProbe.java b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/SpanDecorationProbe.java index 8e4837f1240..57bdcfef16e 100644 --- a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/SpanDecorationProbe.java +++ b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/SpanDecorationProbe.java @@ -143,7 +143,10 @@ public InstrumentationResult.Status instrument( @Override public void evaluate( - CapturedContext context, CapturedContext.Status status, MethodLocation methodLocation) { + CapturedContext context, + CapturedContext.Status status, + MethodLocation methodLocation, + boolean singleProbe) { for (Decoration decoration : decorations) { if (decoration.when != null) { try { diff --git a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/TriggerProbe.java b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/TriggerProbe.java index 5032de9a7b7..8612b00dec9 100644 --- a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/TriggerProbe.java +++ b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/TriggerProbe.java @@ -96,7 +96,10 @@ public TriggerProbe setProbeCondition(ProbeCondition probeCondition) { @Override public void evaluate( - CapturedContext context, CapturedContext.Status status, MethodLocation location) { + CapturedContext context, + CapturedContext.Status status, + MethodLocation location, + boolean singleProbe) { Sampling sampling = getSampling(); if (sampling == null || !sampling.inCoolDown()) { diff --git a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/SnapshotSerializationTest.java b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/SnapshotSerializationTest.java index ab3327ee48f..d5d03b512da 100644 --- a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/SnapshotSerializationTest.java +++ b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/SnapshotSerializationTest.java @@ -127,7 +127,8 @@ public void roundTripCapturedValue() throws IOException, URISyntaxException { new ProbeImplementation.NoopProbeImplementation(PROBE_ID, PROBE_LOCATION), String.class.getTypeName(), -1, - MethodLocation.EXIT); + MethodLocation.EXIT, + false); snapshot.setExit(context); String buffer = adapter.toJson(snapshot); Snapshot deserializedSnapshot = adapter.fromJson(buffer); diff --git a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/exception/DefaultExceptionDebuggerTest.java b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/exception/DefaultExceptionDebuggerTest.java index f4f02e9c7c1..d81a7218963 100644 --- a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/exception/DefaultExceptionDebuggerTest.java +++ b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/exception/DefaultExceptionDebuggerTest.java @@ -367,7 +367,8 @@ private void evalAndCommitProbe(RuntimeException exception, ExceptionProbe excep exceptionProbe.buildLocation(null); CapturedContext capturedContext = new CapturedContext(); capturedContext.addThrowable(exception); - capturedContext.evaluate(exceptionProbe, "", System.currentTimeMillis(), MethodLocation.EXIT); + capturedContext.evaluate( + exceptionProbe, "", System.currentTimeMillis(), MethodLocation.EXIT, false); exceptionProbe.commit(CapturedContext.EMPTY_CAPTURING_CONTEXT, capturedContext, emptyList()); } diff --git a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/probe/LogProbeTest.java b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/probe/LogProbeTest.java index 94ba1547572..b9081c1fa1e 100644 --- a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/probe/LogProbeTest.java +++ b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/probe/LogProbeTest.java @@ -147,8 +147,8 @@ private int runTrace(TracerAPI tracer, boolean captureSnapshot, Integer line, St CapturedContext entryContext = capturedContext(span, logProbe); CapturedContext exitContext = capturedContext(span, logProbe); - logProbe.evaluate(entryContext, new LogStatus(logProbe), MethodLocation.ENTRY); - logProbe.evaluate(exitContext, new LogStatus(logProbe), MethodLocation.EXIT); + logProbe.evaluate(entryContext, new LogStatus(logProbe), MethodLocation.ENTRY, false); + logProbe.evaluate(exitContext, new LogStatus(logProbe), MethodLocation.EXIT, false); int budget = logProbe.isCaptureSnapshot() @@ -193,8 +193,8 @@ private boolean fillSnapshot(DebugSessionStatus status) { CapturedContext entryContext = capturedContext(span, logProbe); CapturedContext exitContext = capturedContext(span, logProbe); - logProbe.evaluate(entryContext, new LogStatus(logProbe), MethodLocation.ENTRY); - logProbe.evaluate(exitContext, new LogStatus(logProbe), MethodLocation.EXIT); + logProbe.evaluate(entryContext, new LogStatus(logProbe), MethodLocation.ENTRY, false); + logProbe.evaluate(exitContext, new LogStatus(logProbe), MethodLocation.EXIT, false); return logProbe.fillSnapshot( entryContext, exitContext, emptyList(), new Snapshot(currentThread(), logProbe, 3)); @@ -204,7 +204,11 @@ private boolean fillSnapshot(DebugSessionStatus status) { private static CapturedContext capturedContext(AgentSpan span, ProbeDefinition probeDefinition) { CapturedContext context = new CapturedContext(); context.evaluate( - probeDefinition, "Log Probe test", System.currentTimeMillis(), MethodLocation.DEFAULT); + probeDefinition, + "Log Probe test", + System.currentTimeMillis(), + MethodLocation.DEFAULT, + false); return context; } @@ -283,7 +287,7 @@ private void fillStatus( private LogStatus prepareContext( CapturedContext context, LogProbe logProbe, MethodLocation methodLocation) { - context.evaluate(logProbe, "", 0, methodLocation); + context.evaluate(logProbe, "", 0, methodLocation, false); return (LogStatus) context.getStatus(PROBE_ID.getEncodedId()); }