From 63a58df27575effdf6282c3f0617fb48218dfce7 Mon Sep 17 00:00:00 2001 From: jean-philippe bempel Date: Thu, 18 Jun 2026 17:18:12 +0200 Subject: [PATCH 1/5] Add evalution timeout for expressions Conditions and expression values (log templates and span decorations) are not capped by a timeout to avoid spending too much time for complex expressions. Refactor TimeoutChecker to be an interface. The former concrete class is now an interface with a static create(Config, Duration) factory that returns either CpuTimeoutChecker or WallTimeoutChecker based on a new config flag (DD_INTERNAL_DYNAMIC_INSTRUMENTATION_TIMEOUT_CHECKER_MODE). Wall clock time can be problematic in case of long GC pauses or sleep or constraint resource (container with less than a CPU). A cpu time driven timeout is more resilient for this. But by default we are using wall clock time for the time being and the cpu time is just an alternative. Refactor Expression evaluation API: All Expression/BooleanExpression implementations now accept a new EvalContext class that bundles ValueReferenceResolver + TimeoutChecker into a single context object. That way we can check the timeout in any expression. NB: for some special expressions (contains on strings or List) we have a per item count limit as we cannot introduce the timeout check for those invocations new config option for the evaluation timeout (DD_DYNAMIC_INSTRUMENTATION_EVALUATION_TIMEOUT), default is 50ms The ValueScript json serialization used for tests was revisited to use a visitor. Tests updated/added across all expression tests, plus new smoke test (LogProbesIntegrationTest) and a configurable TimeoutCheckerTest. --- .../debugger-bootstrap/build.gradle | 1 + .../bootstrap/debugger/DebuggerContext.java | 9 +- .../bootstrap/debugger/el/DebuggerScript.java | 4 +- .../debugger/util/CpuTimeoutChecker.java | 26 ++ .../debugger/util/TimeoutChecker.java | 34 +- .../debugger/util/WallTimeoutChecker.java | 37 +++ .../debugger/util/TimeoutCheckerTest.java | 53 ++- .../el/BooleanValueExpressionAdapter.java | 5 +- .../com/datadog/debugger/el/EvalContext.java | 23 ++ .../com/datadog/debugger/el/Expression.java | 4 +- .../java/com/datadog/debugger/el/Literal.java | 3 +- .../datadog/debugger/el/ProbeCondition.java | 7 +- .../com/datadog/debugger/el/ValueScript.java | 301 +++++++++++++++--- .../el/expressions/BinaryExpression.java | 6 +- .../el/expressions/BinaryOperator.java | 14 +- .../el/expressions/BooleanExpression.java | 6 +- .../CollectionExpressionHelper.java | 8 +- .../el/expressions/ComparisonExpression.java | 14 +- .../el/expressions/ContainsExpression.java | 21 +- .../el/expressions/ExpressionHelper.java | 41 +++ .../FilterCollectionExpression.java | 16 +- .../el/expressions/GetMemberExpression.java | 11 +- .../el/expressions/HasAllExpression.java | 16 +- .../el/expressions/HasAnyExpression.java | 16 +- .../el/expressions/IfElseExpression.java | 13 +- .../debugger/el/expressions/IfExpression.java | 11 +- .../el/expressions/IndexExpression.java | 10 +- .../el/expressions/IsDefinedExpression.java | 10 +- .../el/expressions/IsEmptyExpression.java | 16 +- .../el/expressions/LenExpression.java | 20 +- .../el/expressions/NotExpression.java | 10 +- .../StringPredicateExpression.java | 10 +- .../el/expressions/SubStringExpression.java | 12 +- .../el/expressions/ThenExpression.java | 4 +- .../el/expressions/ValueExpression.java | 6 +- .../el/expressions/ValueRefExpression.java | 11 +- .../el/expressions/WhenExpression.java | 6 +- .../datadog/debugger/el/values/ListValue.java | 14 +- .../datadog/debugger/el/values/MapValue.java | 4 +- .../datadog/debugger/el/values/SetValue.java | 4 +- .../el/BooleanValueExpressionAdapterTest.java | 12 +- ...lverHelper.java => EvalContextHelper.java} | 18 +- .../datadog/debugger/el/ExpressionTest.java | 16 +- .../debugger/el/ProbeConditionTest.java | 107 +++++-- .../datadog/debugger/el/ValueScriptTest.java | 37 ++- .../el/expressions/BinaryExpressionTest.java | 13 +- .../expressions/ComparisonExpressionTest.java | 24 +- .../expressions/ContainsExpressionTest.java | 81 +++-- .../expressions/EndsWithExpressionTest.java | 16 +- .../FilterCollectionExpressionTest.java | 126 +++++--- .../expressions/GetMemberExpressionTest.java | 21 +- .../el/expressions/HasAllExpressionTest.java | 147 ++++++--- .../el/expressions/HasAnyExpressionTest.java | 159 +++++---- .../el/expressions/IfElseExpressionTest.java | 12 +- .../el/expressions/IfExpressionTest.java | 13 +- .../el/expressions/IndexExpressionTest.java | 52 ++- .../expressions/IsDefinedExpressionTest.java | 51 ++- .../el/expressions/IsEmptyExpressionTest.java | 59 ++-- .../el/expressions/LenExpressionTest.java | 18 +- .../el/expressions/MatchesExpressionTest.java | 20 +- .../el/expressions/NotExpressionTest.java | 4 +- .../expressions/StartsWithExpressionTest.java | 16 +- .../expressions/SubStringExpressionTest.java | 18 +- .../expressions/ValueRefExpressionTest.java | 53 +-- .../test/resources/test_conditional_14.json | 3 +- .../test/resources/test_value_expr_01.json | 5 +- .../agent/JsonSnapshotSerializer.java | 4 +- .../debugger/agent/StringTemplateBuilder.java | 8 +- .../com/datadog/debugger/probe/LogProbe.java | 14 +- .../debugger/probe/SpanDecorationProbe.java | 8 +- .../datadog/debugger/probe/TriggerProbe.java | 6 +- .../debugger/util/MoshiSnapshotHelper.java | 14 +- .../debugger/util/SerializerWithLimits.java | 2 +- .../debugger/util/ValueScriptHelper.java | 6 +- .../agent/SnapshotSerializationTest.java | 9 +- .../debugger/el/ELIntegrationSanityTest.java | 9 +- .../debugger/util/StringTokenWriterTest.java | 4 +- .../debugger/DebuggerTestApplication.java | 17 + .../smoketest/LogProbesIntegrationTest.java | 71 +++++ .../smoketest/MoshiConfigTestHelper.java | 15 +- .../SpanDecorationProbesIntegrationTests.java | 2 + .../smoketest/SpanProbesIntegrationTest.java | 6 +- .../datadog/trace/api/ConfigDefaults.java | 2 + .../trace/api/config/DebuggerConfig.java | 4 + .../main/java/datadog/trace/api/Config.java | 21 ++ metadata/supported-configurations.json | 16 + 86 files changed, 1513 insertions(+), 663 deletions(-) create mode 100644 dd-java-agent/agent-debugger/debugger-bootstrap/src/main/java/datadog/trace/bootstrap/debugger/util/CpuTimeoutChecker.java create mode 100644 dd-java-agent/agent-debugger/debugger-bootstrap/src/main/java/datadog/trace/bootstrap/debugger/util/WallTimeoutChecker.java create mode 100644 dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/EvalContext.java rename dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/{RefResolverHelper.java => EvalContextHelper.java} (72%) diff --git a/dd-java-agent/agent-debugger/debugger-bootstrap/build.gradle b/dd-java-agent/agent-debugger/debugger-bootstrap/build.gradle index 3968a161e35..272c7b26f6f 100644 --- a/dd-java-agent/agent-debugger/debugger-bootstrap/build.gradle +++ b/dd-java-agent/agent-debugger/debugger-bootstrap/build.gradle @@ -14,4 +14,5 @@ dependencies { implementation libs.slf4j implementation libs.instrument.java implementation project(':internal-api') + testImplementation libs.bundles.mockito } 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 70f1f8c0347..77ad82b864a 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 @@ -6,7 +6,6 @@ import datadog.trace.bootstrap.instrumentation.api.AgentSpan; import java.lang.reflect.Method; import java.time.Duration; -import java.time.temporal.ChronoUnit; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -324,8 +323,8 @@ public static void evalContext( // snapshot if (needFreeze) { Duration timeout = - Duration.of(Config.get().getDynamicInstrumentationCaptureTimeout(), ChronoUnit.MILLIS); - context.freeze(new TimeoutChecker(timeout)); + Duration.ofMillis(Config.get().getDynamicInstrumentationCaptureTimeout()); + context.freeze(TimeoutChecker.create(Config.get(), timeout)); } } catch (Exception ex) { LOGGER.debug("Error in evalContext: ", ex); @@ -359,8 +358,8 @@ public static void evalContext( // snapshot if (needFreeze) { Duration timeout = - Duration.of(Config.get().getDynamicInstrumentationCaptureTimeout(), ChronoUnit.MILLIS); - context.freeze(new TimeoutChecker(timeout)); + Duration.ofMillis(Config.get().getDynamicInstrumentationCaptureTimeout()); + context.freeze(TimeoutChecker.create(Config.get(), timeout)); } } catch (Exception ex) { LOGGER.debug("Error in evalContext: ", ex); diff --git a/dd-java-agent/agent-debugger/debugger-bootstrap/src/main/java/datadog/trace/bootstrap/debugger/el/DebuggerScript.java b/dd-java-agent/agent-debugger/debugger-bootstrap/src/main/java/datadog/trace/bootstrap/debugger/el/DebuggerScript.java index c4949ef0cb6..b248b440ba4 100644 --- a/dd-java-agent/agent-debugger/debugger-bootstrap/src/main/java/datadog/trace/bootstrap/debugger/el/DebuggerScript.java +++ b/dd-java-agent/agent-debugger/debugger-bootstrap/src/main/java/datadog/trace/bootstrap/debugger/el/DebuggerScript.java @@ -1,10 +1,12 @@ package datadog.trace.bootstrap.debugger.el; +import datadog.trace.bootstrap.debugger.util.TimeoutChecker; + /** * A debugger EL script interface used for communication between the instrumented code and the * debugger EL.
* Because it must be reachable from the instrumented code it must be placed in bootstrap. */ public interface DebuggerScript { - R execute(ValueReferenceResolver valueRefResolver); + R execute(ValueReferenceResolver valueRefResolver, TimeoutChecker timeoutChecker); } diff --git a/dd-java-agent/agent-debugger/debugger-bootstrap/src/main/java/datadog/trace/bootstrap/debugger/util/CpuTimeoutChecker.java b/dd-java-agent/agent-debugger/debugger-bootstrap/src/main/java/datadog/trace/bootstrap/debugger/util/CpuTimeoutChecker.java new file mode 100644 index 00000000000..8ad1a3a111a --- /dev/null +++ b/dd-java-agent/agent-debugger/debugger-bootstrap/src/main/java/datadog/trace/bootstrap/debugger/util/CpuTimeoutChecker.java @@ -0,0 +1,26 @@ +package datadog.trace.bootstrap.debugger.util; + +import java.lang.management.ManagementFactory; +import java.lang.management.ThreadMXBean; +import java.time.Duration; + +public class CpuTimeoutChecker implements TimeoutChecker { + private final Duration timeOut; + private final ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean(); + private final long startCpuTime; + + public CpuTimeoutChecker(Duration timeout) { + this.timeOut = timeout; + startCpuTime = threadMXBean.getCurrentThreadCpuTime(); + } + + @Override + public boolean isTimedOut() { + return threadMXBean.getCurrentThreadCpuTime() - startCpuTime >= timeOut.toNanos(); + } + + @Override + public Duration getTimeOut() { + return null; + } +} diff --git a/dd-java-agent/agent-debugger/debugger-bootstrap/src/main/java/datadog/trace/bootstrap/debugger/util/TimeoutChecker.java b/dd-java-agent/agent-debugger/debugger-bootstrap/src/main/java/datadog/trace/bootstrap/debugger/util/TimeoutChecker.java index f6451afe8c0..a49bb9d3208 100644 --- a/dd-java-agent/agent-debugger/debugger-bootstrap/src/main/java/datadog/trace/bootstrap/debugger/util/TimeoutChecker.java +++ b/dd-java-agent/agent-debugger/debugger-bootstrap/src/main/java/datadog/trace/bootstrap/debugger/util/TimeoutChecker.java @@ -1,33 +1,21 @@ package datadog.trace.bootstrap.debugger.util; +import datadog.trace.api.Config; import java.time.Duration; -import java.time.temporal.ChronoUnit; -public class TimeoutChecker { - public static final Duration DEFAULT_TIME_OUT = Duration.of(100, ChronoUnit.MILLIS); +public interface TimeoutChecker { - private final long start; - private final Duration timeOut; + String CPU = "CPU"; + String WALL = "WALL"; - public TimeoutChecker(Duration timeOut) { - this.start = System.currentTimeMillis(); - this.timeOut = timeOut; - } - - public TimeoutChecker(long start, Duration timeOut) { - this.start = start; - this.timeOut = timeOut; - } + boolean isTimedOut(); - public boolean isTimedOut(long currentTimeMillis) { - return (currentTimeMillis - start) > timeOut.toMillis(); - } - - public long getStart() { - return start; - } + Duration getTimeOut(); - public Duration getTimeOut() { - return timeOut; + static TimeoutChecker create(Config config, Duration timeout) { + if (config.getDynamicInstrumentationTimeoutCheckerMode().equals(CPU)) { + return new CpuTimeoutChecker(timeout); + } + return new WallTimeoutChecker(timeout); } } diff --git a/dd-java-agent/agent-debugger/debugger-bootstrap/src/main/java/datadog/trace/bootstrap/debugger/util/WallTimeoutChecker.java b/dd-java-agent/agent-debugger/debugger-bootstrap/src/main/java/datadog/trace/bootstrap/debugger/util/WallTimeoutChecker.java new file mode 100644 index 00000000000..1bc72b69c45 --- /dev/null +++ b/dd-java-agent/agent-debugger/debugger-bootstrap/src/main/java/datadog/trace/bootstrap/debugger/util/WallTimeoutChecker.java @@ -0,0 +1,37 @@ +package datadog.trace.bootstrap.debugger.util; + +import java.time.Duration; + +public class WallTimeoutChecker implements TimeoutChecker { + + private final long start; + private final Duration timeOut; + + public WallTimeoutChecker(Duration timeOut) { + this.start = System.currentTimeMillis(); + this.timeOut = timeOut; + } + + public WallTimeoutChecker(long start, Duration timeOut) { + this.start = start; + this.timeOut = timeOut; + } + + public boolean isTimedOut(long currentTimeMillis) { + return (currentTimeMillis - start) > timeOut.toMillis(); + } + + @Override + public boolean isTimedOut() { + return isTimedOut(System.currentTimeMillis()); + } + + public long getStart() { + return start; + } + + @Override + public Duration getTimeOut() { + return timeOut; + } +} diff --git a/dd-java-agent/agent-debugger/debugger-bootstrap/src/test/java/datadog/trace/bootstrap/debugger/util/TimeoutCheckerTest.java b/dd-java-agent/agent-debugger/debugger-bootstrap/src/test/java/datadog/trace/bootstrap/debugger/util/TimeoutCheckerTest.java index 10cd94f9042..c3d23678da6 100644 --- a/dd-java-agent/agent-debugger/debugger-bootstrap/src/test/java/datadog/trace/bootstrap/debugger/util/TimeoutCheckerTest.java +++ b/dd-java-agent/agent-debugger/debugger-bootstrap/src/test/java/datadog/trace/bootstrap/debugger/util/TimeoutCheckerTest.java @@ -1,20 +1,55 @@ package datadog.trace.bootstrap.debugger.util; -import static datadog.trace.bootstrap.debugger.util.TimeoutChecker.DEFAULT_TIME_OUT; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; -import org.junit.jupiter.api.Assertions; +import datadog.trace.api.Config; +import java.time.Duration; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.LockSupport; import org.junit.jupiter.api.Test; public class TimeoutCheckerTest { @Test - public void timedOut() { + public void wallTimedOut() { long start = System.currentTimeMillis(); + Duration timeout = Duration.ofMillis(100); // assume that start & timestamp captured in instance below are very close - TimeoutChecker timeoutChecker = new TimeoutChecker(DEFAULT_TIME_OUT); - Assertions.assertFalse(timeoutChecker.isTimedOut(start + 1)); - Assertions.assertTrue(timeoutChecker.isTimedOut(start + DEFAULT_TIME_OUT.toMillis() * 2)); - timeoutChecker = new TimeoutChecker(start, DEFAULT_TIME_OUT); - Assertions.assertFalse(timeoutChecker.isTimedOut(start + 1)); - Assertions.assertTrue(timeoutChecker.isTimedOut(start + DEFAULT_TIME_OUT.toMillis() * 2)); + WallTimeoutChecker timeoutChecker = new WallTimeoutChecker(timeout); + assertFalse(timeoutChecker.isTimedOut(start + 1)); + assertTrue(timeoutChecker.isTimedOut(start + timeout.toMillis() * 2)); + timeoutChecker = new WallTimeoutChecker(start, timeout); + assertFalse(timeoutChecker.isTimedOut(start + 1)); + assertTrue(timeoutChecker.isTimedOut(start + timeout.toMillis() * 2)); + } + + @Test + public void cpuTimedOut() { + CpuTimeoutChecker cpuTimeoutChecker = new CpuTimeoutChecker(Duration.ofMillis(1)); + LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(10)); // not consume cpu + assertFalse(cpuTimeoutChecker.isTimedOut()); + burnCpu(5_000_000); + assertTrue(cpuTimeoutChecker.isTimedOut()); + } + + static volatile int counter; + + private static void burnCpu(int iterations) { + for (int i = 0; i < iterations; i++) { + counter++; + } + } + + @Test + public void configTimeout() { + Config config = mock(Config.class); + when(config.getDynamicInstrumentationTimeoutCheckerMode()).thenReturn(TimeoutChecker.CPU); + assertInstanceOf(CpuTimeoutChecker.class, TimeoutChecker.create(config, Duration.ofMillis(50))); + when(config.getDynamicInstrumentationTimeoutCheckerMode()).thenReturn(TimeoutChecker.WALL); + assertInstanceOf( + WallTimeoutChecker.class, TimeoutChecker.create(config, Duration.ofMillis(50))); } } diff --git a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/BooleanValueExpressionAdapter.java b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/BooleanValueExpressionAdapter.java index c71c1b9b8de..e7986715e24 100644 --- a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/BooleanValueExpressionAdapter.java +++ b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/BooleanValueExpressionAdapter.java @@ -3,7 +3,6 @@ import com.datadog.debugger.el.expressions.BooleanExpression; import com.datadog.debugger.el.expressions.ValueExpression; import com.datadog.debugger.el.values.BooleanValue; -import datadog.trace.bootstrap.debugger.el.ValueReferenceResolver; public class BooleanValueExpressionAdapter implements ValueExpression { @@ -14,8 +13,8 @@ public BooleanValueExpressionAdapter(BooleanExpression booleanExpression) { } @Override - public BooleanValue evaluate(ValueReferenceResolver valueRefResolver) { - Boolean result = booleanExpression.evaluate(valueRefResolver); + public BooleanValue evaluate(EvalContext evalContext) { + Boolean result = booleanExpression.evaluate(evalContext); if (result == null) { throw new EvaluationException( "Boolean expression returning null", PrettyPrintVisitor.print(this)); diff --git a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/EvalContext.java b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/EvalContext.java new file mode 100644 index 00000000000..bd0ece583f2 --- /dev/null +++ b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/EvalContext.java @@ -0,0 +1,23 @@ +package com.datadog.debugger.el; + +import datadog.trace.bootstrap.debugger.el.ValueReferenceResolver; +import datadog.trace.bootstrap.debugger.util.TimeoutChecker; + +public class EvalContext { + private final ValueReferenceResolver valueRefResolver; + private final TimeoutChecker timeoutChecker; + + public EvalContext( + final ValueReferenceResolver valueRefResolver, final TimeoutChecker timeoutChecker) { + this.valueRefResolver = valueRefResolver; + this.timeoutChecker = timeoutChecker; + } + + public ValueReferenceResolver getValueRefResolver() { + return valueRefResolver; + } + + public TimeoutChecker getTimeoutChecker() { + return timeoutChecker; + } +} diff --git a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/Expression.java b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/Expression.java index f9d29f0687a..d9b7b2e766f 100644 --- a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/Expression.java +++ b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/Expression.java @@ -1,11 +1,9 @@ package com.datadog.debugger.el; -import datadog.trace.bootstrap.debugger.el.ValueReferenceResolver; - /** Represents any evaluable expression */ @FunctionalInterface public interface Expression { - ReturnType evaluate(ValueReferenceResolver valueRefResolver); + ReturnType evaluate(EvalContext evalContext); default R accept(Visitor visitor) { return null; diff --git a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/Literal.java b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/Literal.java index 6d5c8d2fca1..cfc3b9971d9 100644 --- a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/Literal.java +++ b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/Literal.java @@ -1,7 +1,6 @@ package com.datadog.debugger.el; import com.datadog.debugger.el.expressions.ValueExpression; -import datadog.trace.bootstrap.debugger.el.ValueReferenceResolver; import datadog.trace.bootstrap.debugger.el.Values; import java.util.Objects; @@ -27,7 +26,7 @@ public boolean isUndefined() { } @Override - public Value evaluate(ValueReferenceResolver valueRefResolver) { + public Value evaluate(EvalContext evalContext) { return this; } diff --git a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/ProbeCondition.java b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/ProbeCondition.java index f81c92a8b19..7ca052c2cab 100644 --- a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/ProbeCondition.java +++ b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/ProbeCondition.java @@ -9,6 +9,7 @@ import com.squareup.moshi.JsonWriter; import datadog.trace.bootstrap.debugger.el.DebuggerScript; import datadog.trace.bootstrap.debugger.el.ValueReferenceResolver; +import datadog.trace.bootstrap.debugger.util.TimeoutChecker; import java.io.IOException; import javax.annotation.Nonnull; @@ -99,12 +100,12 @@ public static ProbeCondition load(JsonReader reader) throws IOException { } @Override - public Boolean execute(ValueReferenceResolver valueRefResolver) { + public Boolean execute(ValueReferenceResolver valueRefResolver, TimeoutChecker timeoutChecker) { if (when == null) { return true; } - if (when.evaluate(valueRefResolver)) { - then.evaluate(valueRefResolver); + if (when.evaluate(new EvalContext(valueRefResolver, timeoutChecker))) { + then.evaluate(new EvalContext(valueRefResolver, timeoutChecker)); return true; } return false; diff --git a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/ValueScript.java b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/ValueScript.java index bcc940e1d5d..c446e0b12e5 100644 --- a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/ValueScript.java +++ b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/ValueScript.java @@ -1,16 +1,43 @@ package com.datadog.debugger.el; +import com.datadog.debugger.el.expressions.BinaryExpression; +import com.datadog.debugger.el.expressions.BinaryOperator; +import com.datadog.debugger.el.expressions.BooleanExpression; +import com.datadog.debugger.el.expressions.ComparisonExpression; +import com.datadog.debugger.el.expressions.ComparisonOperator; +import com.datadog.debugger.el.expressions.ContainsExpression; +import com.datadog.debugger.el.expressions.EndsWithExpression; +import com.datadog.debugger.el.expressions.FilterCollectionExpression; import com.datadog.debugger.el.expressions.GetMemberExpression; +import com.datadog.debugger.el.expressions.HasAllExpression; +import com.datadog.debugger.el.expressions.HasAnyExpression; +import com.datadog.debugger.el.expressions.IfElseExpression; +import com.datadog.debugger.el.expressions.IfExpression; import com.datadog.debugger.el.expressions.IndexExpression; +import com.datadog.debugger.el.expressions.IsDefinedExpression; +import com.datadog.debugger.el.expressions.IsEmptyExpression; import com.datadog.debugger.el.expressions.LenExpression; +import com.datadog.debugger.el.expressions.MatchesExpression; +import com.datadog.debugger.el.expressions.NotExpression; +import com.datadog.debugger.el.expressions.StartsWithExpression; +import com.datadog.debugger.el.expressions.SubStringExpression; import com.datadog.debugger.el.expressions.ValueExpression; import com.datadog.debugger.el.expressions.ValueRefExpression; +import com.datadog.debugger.el.expressions.WhenExpression; +import com.datadog.debugger.el.values.BooleanValue; +import com.datadog.debugger.el.values.ListValue; +import com.datadog.debugger.el.values.MapValue; +import com.datadog.debugger.el.values.NullValue; +import com.datadog.debugger.el.values.NumericValue; +import com.datadog.debugger.el.values.ObjectValue; +import com.datadog.debugger.el.values.SetValue; import com.datadog.debugger.el.values.StringValue; import com.squareup.moshi.JsonAdapter; import com.squareup.moshi.JsonReader; import com.squareup.moshi.JsonWriter; import datadog.trace.bootstrap.debugger.el.DebuggerScript; import datadog.trace.bootstrap.debugger.el.ValueReferenceResolver; +import datadog.trace.bootstrap.debugger.util.TimeoutChecker; import java.io.IOException; import java.util.Objects; import java.util.regex.Matcher; @@ -37,8 +64,8 @@ public ValueExpression getExpr() { } @Override - public Value execute(ValueReferenceResolver valueRefResolver) { - return expr.evaluate(valueRefResolver); + public Value execute(ValueReferenceResolver valueRefResolver, TimeoutChecker timeoutChecker) { + return expr.evaluate(new EvalContext(valueRefResolver, timeoutChecker)); } @Override @@ -127,46 +154,248 @@ public void toJson(JsonWriter jsonWriter, ValueScript value) throws IOException jsonWriter.name("dsl"); jsonWriter.value(value.dsl); jsonWriter.name("json"); - writeValueExpression(jsonWriter, value.expr); + ToJsonVisitor toJsonVisitor = new ToJsonVisitor(jsonWriter); + value.expr.accept(toJsonVisitor); jsonWriter.endObject(); } - private void writeValueExpression(JsonWriter jsonWriter, ValueExpression expr) - throws IOException { - if (expr instanceof Value) { - if (expr instanceof StringValue) { - jsonWriter.value(((StringValue) expr).getValue()); - } else { - throw new IOException("Unsupported operation: " + expr.getClass().getTypeName()); + private static class ToJsonVisitor implements Visitor { + private final JsonWriter jsonWriter; + + public ToJsonVisitor(JsonWriter jsonWriter) { + this.jsonWriter = jsonWriter; + } + + @Override + public Void visit(BinaryExpression binaryExpression) { + throw new UnsupportedOperationException("BinaryExpression is not supported"); + } + + @Override + public Void visit(BinaryOperator operator) { + throw new UnsupportedOperationException("BinaryOperator is not supported"); + } + + @Override + public Void visit(ComparisonExpression comparisonExpression) { + try { + jsonWriter.beginObject(); + comparisonExpression.getOperator().accept(this); + jsonWriter.beginArray(); + comparisonExpression.getLeft().accept(this); + comparisonExpression.getRight().accept(this); + jsonWriter.endArray(); + jsonWriter.endObject(); + } catch (IOException e) { + throw new RuntimeException(e); } - return; + return null; } - jsonWriter.beginObject(); - if (expr instanceof ValueRefExpression) { - ValueRefExpression valueRefExpr = (ValueRefExpression) expr; - jsonWriter.name("ref"); - jsonWriter.value(valueRefExpr.getSymbolName()); - } else if (expr instanceof GetMemberExpression) { - GetMemberExpression getMemberExpr = (GetMemberExpression) expr; - jsonWriter.name("getmember"); - jsonWriter.beginArray(); - writeValueExpression(jsonWriter, getMemberExpr.getTarget()); - jsonWriter.value(getMemberExpr.getMemberName()); - jsonWriter.endArray(); - } else if (expr instanceof LenExpression) { - jsonWriter.name("count"); - writeValueExpression(jsonWriter, ((LenExpression) expr).getSource()); - } else if (expr instanceof IndexExpression) { - IndexExpression idxExpr = (IndexExpression) expr; - jsonWriter.name("index"); - jsonWriter.beginArray(); - writeValueExpression(jsonWriter, idxExpr.getTarget()); - writeValueExpression(jsonWriter, idxExpr.getKey()); - jsonWriter.endArray(); - } else { - throw new IOException("Unsupported operation: " + expr.getClass().getTypeName()); + + @Override + public Void visit(ComparisonOperator operator) { + try { + jsonWriter.name(operator.name().toLowerCase()); + } catch (IOException e) { + throw new RuntimeException(e); + } + return null; + } + + @Override + public Void visit(ContainsExpression containsExpression) { + throw new UnsupportedOperationException("ContainsExpression is not supported"); + } + + @Override + public Void visit(EndsWithExpression endsWithExpression) { + throw new UnsupportedOperationException("EndsWithExpression is not supported"); + } + + @Override + public Void visit(FilterCollectionExpression filterCollectionExpression) { + try { + jsonWriter.beginObject(); + jsonWriter.name("filter"); + jsonWriter.beginArray(); + filterCollectionExpression.getSource().accept(this); + filterCollectionExpression.getFilterExpression().accept(this); + jsonWriter.endArray(); + jsonWriter.endObject(); + } catch (IOException e) { + throw new RuntimeException(e); + } + return null; + } + + @Override + public Void visit(HasAllExpression hasAllExpression) { + throw new UnsupportedOperationException("HasAllExpression is not supported"); + } + + @Override + public Void visit(HasAnyExpression hasAnyExpression) { + throw new UnsupportedOperationException("HasAnyExpression is not supported"); + } + + @Override + public Void visit(IfElseExpression ifElseExpression) { + throw new UnsupportedOperationException("IfElseExpression is not supported"); + } + + @Override + public Void visit(IfExpression ifExpression) { + throw new UnsupportedOperationException("IfExpression is not supported"); + } + + @Override + public Void visit(IsEmptyExpression isEmptyExpression) { + throw new UnsupportedOperationException("IsEmptyExpression is not supported"); + } + + @Override + public Void visit(IsDefinedExpression isDefinedExpression) { + throw new UnsupportedOperationException("IsDefinedExpression is not supported"); + } + + @Override + public Void visit(LenExpression lenExpression) { + try { + jsonWriter.beginObject(); + jsonWriter.name("count"); + lenExpression.getSource().accept(this); + jsonWriter.endObject(); + } catch (IOException e) { + throw new RuntimeException(e); + } + return null; + } + + @Override + public Void visit(MatchesExpression matchesExpression) { + throw new UnsupportedOperationException("MatchesExpression is not supported"); + } + + @Override + public Void visit(NotExpression notExpression) { + throw new UnsupportedOperationException("NotExpression is not supported"); + } + + @Override + public Void visit(StartsWithExpression startsWithExpression) { + throw new UnsupportedOperationException("StartsWithExpression is not supported"); + } + + @Override + public Void visit(SubStringExpression subStringExpression) { + throw new UnsupportedOperationException("SubStringExpression is not supported"); + } + + @Override + public Void visit(ValueRefExpression valueRefExpression) { + try { + jsonWriter.beginObject(); + jsonWriter.name("ref"); + jsonWriter.value(valueRefExpression.getSymbolName()); + jsonWriter.endObject(); + } catch (IOException e) { + throw new RuntimeException(e); + } + return null; + } + + @Override + public Void visit(GetMemberExpression getMemberExpression) { + try { + jsonWriter.beginObject(); + jsonWriter.name("getmember"); + jsonWriter.beginArray(); + getMemberExpression.getTarget().accept(this); + jsonWriter.value(getMemberExpression.getMemberName()); + jsonWriter.endArray(); + jsonWriter.endObject(); + } catch (IOException e) { + throw new RuntimeException(e); + } + return null; + } + + @Override + public Void visit(IndexExpression indexExpression) { + try { + jsonWriter.beginObject(); + jsonWriter.name("index"); + jsonWriter.beginArray(); + indexExpression.getTarget().accept(this); + indexExpression.getKey().accept(this); + jsonWriter.endArray(); + jsonWriter.endObject(); + } catch (IOException e) { + throw new RuntimeException(e); + } + return null; + } + + @Override + public Void visit(WhenExpression whenExpression) { + throw new UnsupportedOperationException("WhenExpression is not supported"); + } + + @Override + public Void visit(BooleanExpression booleanExpression) { + throw new UnsupportedOperationException("BooleanExpression is not supported"); + } + + @Override + public Void visit(ObjectValue objectValue) { + throw new UnsupportedOperationException("ObjectValue is not supported"); + } + + @Override + public Void visit(StringValue stringValue) { + try { + jsonWriter.value(stringValue.getValue()); + } catch (IOException e) { + throw new RuntimeException(e); + } + return null; + } + + @Override + public Void visit(NumericValue numericValue) { + try { + // TODO handle double + jsonWriter.value(numericValue.getValue().longValue()); + } catch (IOException e) { + throw new RuntimeException(e); + } + return null; + } + + @Override + public Void visit(BooleanValue booleanValue) { + throw new UnsupportedOperationException("BooleanValue is not supported"); + } + + @Override + public Void visit(NullValue nullValue) { + throw new UnsupportedOperationException("NullValue is not supported"); + } + + @Override + public Void visit(ListValue listValue) { + throw new UnsupportedOperationException("ListValue is not supported"); + } + + @Override + public Void visit(MapValue mapValue) { + throw new UnsupportedOperationException("MapValue is not supported"); + } + + @Override + public Void visit(SetValue setValue) { + throw new UnsupportedOperationException("SetValue is not supported"); } - jsonWriter.endObject(); } } } diff --git a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/BinaryExpression.java b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/BinaryExpression.java index d4063ec5cd2..3c955d21b74 100644 --- a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/BinaryExpression.java +++ b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/BinaryExpression.java @@ -1,7 +1,7 @@ package com.datadog.debugger.el.expressions; +import com.datadog.debugger.el.EvalContext; import com.datadog.debugger.el.Visitor; -import datadog.trace.bootstrap.debugger.el.ValueReferenceResolver; /** * Takes two {@linkplain BooleanExpression} instances and combines them with the given {@link @@ -20,8 +20,8 @@ public BinaryExpression( } @Override - public Boolean evaluate(ValueReferenceResolver valueRefResolver) { - return operator.apply(left, right, valueRefResolver); + public Boolean evaluate(EvalContext evalContext) { + return operator.apply(left, right, evalContext); } @Override diff --git a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/BinaryOperator.java b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/BinaryOperator.java index ce5784b918b..3a1a7391d17 100644 --- a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/BinaryOperator.java +++ b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/BinaryOperator.java @@ -1,21 +1,19 @@ package com.datadog.debugger.el.expressions; +import com.datadog.debugger.el.EvalContext; import com.datadog.debugger.el.Visitor; -import datadog.trace.bootstrap.debugger.el.ValueReferenceResolver; public enum BinaryOperator { AND("&&") { @Override - public Boolean apply( - BooleanExpression left, BooleanExpression right, ValueReferenceResolver resolver) { - return left.evaluate(resolver) && right.evaluate(resolver); + public Boolean apply(BooleanExpression left, BooleanExpression right, EvalContext evalContext) { + return left.evaluate(evalContext) && right.evaluate(evalContext); } }, OR("||") { @Override - public Boolean apply( - BooleanExpression left, BooleanExpression right, ValueReferenceResolver resolver) { - return left.evaluate(resolver) || right.evaluate(resolver); + public Boolean apply(BooleanExpression left, BooleanExpression right, EvalContext evalContext) { + return left.evaluate(evalContext) || right.evaluate(evalContext); } }; @@ -26,7 +24,7 @@ public Boolean apply( } public abstract Boolean apply( - BooleanExpression left, BooleanExpression right, ValueReferenceResolver resolver); + BooleanExpression left, BooleanExpression right, EvalContext evalContext); public R accept(Visitor visitor) { return visitor.visit(this); diff --git a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/BooleanExpression.java b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/BooleanExpression.java index 3ff97e2d890..2f98571f03b 100644 --- a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/BooleanExpression.java +++ b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/BooleanExpression.java @@ -1,15 +1,15 @@ package com.datadog.debugger.el.expressions; +import com.datadog.debugger.el.EvalContext; import com.datadog.debugger.el.Expression; import com.datadog.debugger.el.Visitor; -import datadog.trace.bootstrap.debugger.el.ValueReferenceResolver; /** A generic interface for expressions resolving to {@linkplain Boolean} */ public interface BooleanExpression extends Expression { BooleanExpression TRUE = new BooleanExpression() { @Override - public Boolean evaluate(ValueReferenceResolver valueRefResolver) { + public Boolean evaluate(EvalContext evalContext) { return Boolean.TRUE; } @@ -26,7 +26,7 @@ public R accept(Visitor visitor) { BooleanExpression FALSE = new BooleanExpression() { @Override - public Boolean evaluate(ValueReferenceResolver valueRefResolver) { + public Boolean evaluate(EvalContext evalContext) { return Boolean.FALSE; } diff --git a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/CollectionExpressionHelper.java b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/CollectionExpressionHelper.java index c811c82fa73..540008c6ea1 100644 --- a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/CollectionExpressionHelper.java +++ b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/CollectionExpressionHelper.java @@ -2,13 +2,13 @@ import static com.datadog.debugger.el.PrettyPrintVisitor.print; +import com.datadog.debugger.el.EvalContext; import com.datadog.debugger.el.EvaluationException; import com.datadog.debugger.el.Expression; import com.datadog.debugger.el.Value; import com.datadog.debugger.el.values.ListValue; import com.datadog.debugger.el.values.MapValue; import com.datadog.debugger.el.values.SetValue; -import datadog.trace.bootstrap.debugger.el.ValueReferenceResolver; import datadog.trace.bootstrap.debugger.util.WellKnownClasses; import java.util.List; import java.util.Map; @@ -34,14 +34,12 @@ public static void checkSupportedList(ListValue collection, Expression expres } public static Value evaluateTargetCollection( - ValueExpression collectionTarget, - Expression expression, - ValueReferenceResolver valueRefResolver) { + ValueExpression collectionTarget, Expression expression, EvalContext evalContext) { if (collectionTarget == null) { throw new EvaluationException( "Cannot evaluate the expression for null value", print(expression)); } - Value value = collectionTarget.evaluate(valueRefResolver); + Value value = collectionTarget.evaluate(evalContext); if (value.isUndefined()) { throw new EvaluationException( "Cannot evaluate the expression for undefined value", print(expression)); diff --git a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/ComparisonExpression.java b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/ComparisonExpression.java index b595921d928..8a13febaad2 100644 --- a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/ComparisonExpression.java +++ b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/ComparisonExpression.java @@ -1,10 +1,12 @@ package com.datadog.debugger.el.expressions; +import static com.datadog.debugger.el.expressions.ExpressionHelper.checkTimeout; + +import com.datadog.debugger.el.EvalContext; import com.datadog.debugger.el.EvaluationException; import com.datadog.debugger.el.PrettyPrintVisitor; import com.datadog.debugger.el.Value; import com.datadog.debugger.el.Visitor; -import datadog.trace.bootstrap.debugger.el.ValueReferenceResolver; /** * Takes two {@linkplain ValueExpression} instances and compares them using the given {@link @@ -23,17 +25,19 @@ public ComparisonExpression( } @Override - public Boolean evaluate(ValueReferenceResolver valueRefResolver) { - Value leftValue = left.evaluate(valueRefResolver); + public Boolean evaluate(EvalContext evalContext) { + Value leftValue = left.evaluate(evalContext); if (leftValue.isUndefined()) { return Boolean.FALSE; } - Value rightValue = right.evaluate(valueRefResolver); + Value rightValue = right.evaluate(evalContext); if (rightValue.isUndefined()) { return Boolean.FALSE; } try { - return operator.apply(leftValue, rightValue); + boolean result = operator.apply(leftValue, rightValue); + checkTimeout(evalContext.getTimeoutChecker(), this); + return result; } catch (EvaluationException e) { throw new EvaluationException(e.getMessage(), PrettyPrintVisitor.print(this)); } diff --git a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/ContainsExpression.java b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/ContainsExpression.java index d2fc6a62bbf..bde8654b551 100644 --- a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/ContainsExpression.java +++ b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/ContainsExpression.java @@ -1,11 +1,14 @@ package com.datadog.debugger.el.expressions; +import static com.datadog.debugger.el.expressions.ExpressionHelper.checkStringLength; +import static com.datadog.debugger.el.expressions.ExpressionHelper.checkTimeout; + +import com.datadog.debugger.el.EvalContext; import com.datadog.debugger.el.EvaluationException; import com.datadog.debugger.el.PrettyPrintVisitor; import com.datadog.debugger.el.Value; import com.datadog.debugger.el.Visitor; import com.datadog.debugger.el.values.CollectionValue; -import datadog.trace.bootstrap.debugger.el.ValueReferenceResolver; public class ContainsExpression implements BooleanExpression { private final ValueExpression target; @@ -17,8 +20,8 @@ public ContainsExpression(ValueExpression target, ValueExpression value) { } @Override - public Boolean evaluate(ValueReferenceResolver valueRefResolver) { - Value targetValue = target.evaluate(valueRefResolver); + public Boolean evaluate(EvalContext evalContext) { + Value targetValue = target.evaluate(evalContext); if (targetValue.isUndefined()) { throw new EvaluationException( "Cannot evaluate the expression for undefined value", PrettyPrintVisitor.print(this)); @@ -27,22 +30,28 @@ public Boolean evaluate(ValueReferenceResolver valueRefResolver) { throw new EvaluationException( "Cannot evaluate the expression for null value", PrettyPrintVisitor.print(this)); } - Value val = value.evaluate(valueRefResolver); + Value val = value.evaluate(evalContext); if (val.isUndefined()) { return false; } + boolean result; if (targetValue.getValue() instanceof String) { String targetStr = (String) targetValue.getValue(); if (val.getValue() instanceof String) { String valStr = (String) val.getValue(); - return targetStr.contains(valStr); + checkStringLength(valStr, this); + result = targetStr.contains(valStr); + checkTimeout(evalContext.getTimeoutChecker(), this); + return result; } throw new EvaluationException( "Cannot evaluate the expression for non-string value", PrettyPrintVisitor.print(this)); } if (targetValue instanceof CollectionValue) { try { - return ((CollectionValue) targetValue).contains(val); + result = ((CollectionValue) targetValue).contains(val); + checkTimeout(evalContext.getTimeoutChecker(), this); + return result; } catch (RuntimeException ex) { throw new EvaluationException(ex.getMessage(), PrettyPrintVisitor.print(this)); } diff --git a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/ExpressionHelper.java b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/ExpressionHelper.java index 43d5930dcb6..10b7a73911c 100644 --- a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/ExpressionHelper.java +++ b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/ExpressionHelper.java @@ -1,13 +1,54 @@ package com.datadog.debugger.el.expressions; +import com.datadog.debugger.el.EvaluationException; import com.datadog.debugger.el.Expression; import com.datadog.debugger.el.PrettyPrintVisitor; import com.datadog.debugger.el.RedactedException; +import datadog.trace.bootstrap.debugger.util.TimeoutChecker; +import java.util.Collection; public class ExpressionHelper { + public static final int MAX_COLLECTION_ITEMS = 1_000_000; + public static final int MAX_ARRAY_ITEMS = 1_000_000; + public static final int MAX_STRING_LENGTH = 100_000_000; + public static void throwRedactedException(Expression expr) { String strExpr = PrettyPrintVisitor.print(expr); throw new RedactedException( "Could not evaluate the expression because '" + strExpr + "' was redacted", strExpr); } + + public static void checkTimeout(TimeoutChecker checker, Expression expr) { + if (checker.isTimedOut()) { + throw new EvaluationException( + "timeout (" + checker.getTimeOut().toMillis() + "ms)", PrettyPrintVisitor.print(expr)); + } + } + + public static void checkStringLength(String val, Expression expr) { + if (val == null) { + return; + } + if (val.length() > MAX_STRING_LENGTH) { + throw new EvaluationException( + "string too large (>" + MAX_STRING_LENGTH + ")", PrettyPrintVisitor.print(expr)); + } + } + + public static void checkCollectionSize(Collection collection, Expression expr) { + if (collection == null) { + return; + } + if (collection.size() > MAX_COLLECTION_ITEMS) { + throw new EvaluationException( + "Collection too large (>" + MAX_COLLECTION_ITEMS + ")", PrettyPrintVisitor.print(expr)); + } + } + + public static void checkArrayLength(int arrayLength, Expression expr) { + if (arrayLength > MAX_ARRAY_ITEMS) { + throw new EvaluationException( + "Array too large (>" + MAX_ARRAY_ITEMS + ")", PrettyPrintVisitor.print(expr)); + } + } } diff --git a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/FilterCollectionExpression.java b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/FilterCollectionExpression.java index 5c3e27ee173..cd4c8ef1c89 100644 --- a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/FilterCollectionExpression.java +++ b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/FilterCollectionExpression.java @@ -5,7 +5,9 @@ import static com.datadog.debugger.el.expressions.CollectionExpressionHelper.checkSupportedMap; import static com.datadog.debugger.el.expressions.CollectionExpressionHelper.checkSupportedSet; import static com.datadog.debugger.el.expressions.CollectionExpressionHelper.evaluateTargetCollection; +import static com.datadog.debugger.el.expressions.ExpressionHelper.checkTimeout; +import com.datadog.debugger.el.EvalContext; import com.datadog.debugger.el.EvaluationException; import com.datadog.debugger.el.Value; import com.datadog.debugger.el.Visitor; @@ -41,8 +43,9 @@ public FilterCollectionExpression(ValueExpression source, BooleanExpression f } @Override - public CollectionValue evaluate(ValueReferenceResolver valueRefResolver) { - Value collectionValue = evaluateTargetCollection(source, filterExpression, valueRefResolver); + public CollectionValue evaluate(EvalContext evalContext) { + Value collectionValue = evaluateTargetCollection(source, filterExpression, evalContext); + ValueReferenceResolver valueRefResolver = evalContext.getValueRefResolver(); if (collectionValue instanceof ListValue) { ListValue materialized = (ListValue) collectionValue; checkSupportedList(materialized, this); @@ -53,9 +56,10 @@ public CollectionValue evaluate(ValueReferenceResolver valueRefResolver) { Object value = materialized.get(i).getValue(); valueRefResolver.addExtension( ValueReferences.ITERATOR_EXTENSION_NAME, CapturedValue.of(value)); - if (filterExpression.evaluate(valueRefResolver)) { + if (filterExpression.evaluate(evalContext)) { filtered.add(value); } + checkTimeout(evalContext.getTimeoutChecker(), this); } } finally { valueRefResolver.removeExtension(ValueReferences.ITERATOR_EXTENSION_NAME); @@ -74,9 +78,10 @@ public CollectionValue evaluate(ValueReferenceResolver valueRefResolver) { valueRefResolver.addExtension( ValueReferences.ITERATOR_EXTENSION_NAME, CapturedValue.of(new MapValue.Entry(key, value))); - if (filterExpression.evaluate(valueRefResolver)) { + if (filterExpression.evaluate(evalContext)) { filtered.put(key.getValue(), value.getValue()); } + checkTimeout(evalContext.getTimeoutChecker(), this); } } finally { valueRefResolver.removeExtension(ValueReferences.KEY_EXTENSION_NAME); @@ -92,9 +97,10 @@ public CollectionValue evaluate(ValueReferenceResolver valueRefResolver) { for (Object value : setHolder) { valueRefResolver.addExtension( ValueReferences.ITERATOR_EXTENSION_NAME, CapturedValue.of(value)); - if (filterExpression.evaluate(valueRefResolver)) { + if (filterExpression.evaluate(evalContext)) { filtered.add(value); } + checkTimeout(evalContext.getTimeoutChecker(), this); } } finally { valueRefResolver.removeExtension(ValueReferences.ITERATOR_EXTENSION_NAME); diff --git a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/GetMemberExpression.java b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/GetMemberExpression.java index 45609979894..bfcfe6ee826 100644 --- a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/GetMemberExpression.java +++ b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/GetMemberExpression.java @@ -1,5 +1,8 @@ package com.datadog.debugger.el.expressions; +import static com.datadog.debugger.el.expressions.ExpressionHelper.checkTimeout; + +import com.datadog.debugger.el.EvalContext; import com.datadog.debugger.el.EvaluationException; import com.datadog.debugger.el.Generated; import com.datadog.debugger.el.PrettyPrintVisitor; @@ -7,7 +10,6 @@ import com.datadog.debugger.el.ValueType; import com.datadog.debugger.el.Visitor; import datadog.trace.bootstrap.debugger.CapturedContext; -import datadog.trace.bootstrap.debugger.el.ValueReferenceResolver; import datadog.trace.bootstrap.debugger.util.Redaction; import java.util.Objects; @@ -21,14 +23,14 @@ public GetMemberExpression(ValueExpression target, String memberName) { } @Override - public Value evaluate(ValueReferenceResolver valueRefResolver) { - Value targetValue = target.evaluate(valueRefResolver); + public Value evaluate(EvalContext evalContext) { + Value targetValue = target.evaluate(evalContext); if (targetValue == Value.undefined()) { return targetValue; } CapturedContext.CapturedValue member; try { - member = valueRefResolver.getMember(targetValue.getValue(), memberName); + member = evalContext.getValueRefResolver().getMember(targetValue.getValue(), memberName); } catch (RuntimeException ex) { throw new EvaluationException(ex.getMessage(), PrettyPrintVisitor.print(this), ex); } @@ -41,6 +43,7 @@ public Value evaluate(ValueReferenceResolver valueRefResolver) { || Redaction.isRedactedType(memberValue.getClass().getTypeName()))) { ExpressionHelper.throwRedactedException(this); } + checkTimeout(evalContext.getTimeoutChecker(), this); return Value.of(member.getValue(), ValueType.of(member.getType())); } diff --git a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/HasAllExpression.java b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/HasAllExpression.java index 6e2c449343f..90bba2f6165 100644 --- a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/HasAllExpression.java +++ b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/HasAllExpression.java @@ -5,7 +5,9 @@ import static com.datadog.debugger.el.expressions.CollectionExpressionHelper.checkSupportedMap; import static com.datadog.debugger.el.expressions.CollectionExpressionHelper.checkSupportedSet; import static com.datadog.debugger.el.expressions.CollectionExpressionHelper.evaluateTargetCollection; +import static com.datadog.debugger.el.expressions.ExpressionHelper.checkTimeout; +import com.datadog.debugger.el.EvalContext; import com.datadog.debugger.el.EvaluationException; import com.datadog.debugger.el.Value; import com.datadog.debugger.el.Visitor; @@ -30,8 +32,9 @@ public HasAllExpression(ValueExpression valueExpression, BooleanExpression fi } @Override - public Boolean evaluate(ValueReferenceResolver valueRefResolver) { - Value value = evaluateTargetCollection(valueExpression, this, valueRefResolver); + public Boolean evaluate(EvalContext evalContext) { + Value value = evaluateTargetCollection(valueExpression, this, evalContext); + ValueReferenceResolver valueRefResolver = evalContext.getValueRefResolver(); if (value instanceof ListValue) { ListValue collection = (ListValue) value; checkSupportedList(collection, this); @@ -44,9 +47,10 @@ public Boolean evaluate(ValueReferenceResolver valueRefResolver) { for (int i = 0; i < len; i++) { valueRefResolver.addExtension( ValueReferences.ITERATOR_EXTENSION_NAME, CapturedValue.of(collection.get(i))); - if (!filterPredicateExpression.evaluate(valueRefResolver)) { + if (!filterPredicateExpression.evaluate(evalContext)) { return Boolean.FALSE; } + checkTimeout(evalContext.getTimeoutChecker(), this); } return Boolean.TRUE; } finally { @@ -69,9 +73,10 @@ public Boolean evaluate(ValueReferenceResolver valueRefResolver) { valueRefResolver.addExtension( ValueReferences.ITERATOR_EXTENSION_NAME, CapturedValue.of(new MapValue.Entry(key, val))); - if (!filterPredicateExpression.evaluate(valueRefResolver)) { + if (!filterPredicateExpression.evaluate(evalContext)) { return Boolean.FALSE; } + checkTimeout(evalContext.getTimeoutChecker(), this); } return Boolean.TRUE; } finally { @@ -91,9 +96,10 @@ public Boolean evaluate(ValueReferenceResolver valueRefResolver) { for (Object val : setHolder) { valueRefResolver.addExtension( ValueReferences.ITERATOR_EXTENSION_NAME, CapturedValue.of(val)); - if (!filterPredicateExpression.evaluate(valueRefResolver)) { + if (!filterPredicateExpression.evaluate(evalContext)) { return Boolean.FALSE; } + checkTimeout(evalContext.getTimeoutChecker(), this); } return Boolean.TRUE; } finally { diff --git a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/HasAnyExpression.java b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/HasAnyExpression.java index 23c3dea831f..7de0e110dfa 100644 --- a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/HasAnyExpression.java +++ b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/HasAnyExpression.java @@ -5,7 +5,9 @@ import static com.datadog.debugger.el.expressions.CollectionExpressionHelper.checkSupportedMap; import static com.datadog.debugger.el.expressions.CollectionExpressionHelper.checkSupportedSet; import static com.datadog.debugger.el.expressions.CollectionExpressionHelper.evaluateTargetCollection; +import static com.datadog.debugger.el.expressions.ExpressionHelper.checkTimeout; +import com.datadog.debugger.el.EvalContext; import com.datadog.debugger.el.EvaluationException; import com.datadog.debugger.el.Value; import com.datadog.debugger.el.Visitor; @@ -31,8 +33,9 @@ public HasAnyExpression( } @Override - public Boolean evaluate(ValueReferenceResolver valueRefResolver) { - Value value = evaluateTargetCollection(valueExpression, this, valueRefResolver); + public Boolean evaluate(EvalContext evalContext) { + Value value = evaluateTargetCollection(valueExpression, this, evalContext); + ValueReferenceResolver valueRefResolver = evalContext.getValueRefResolver(); if (value instanceof ListValue) { ListValue collection = (ListValue) value; checkSupportedList(collection, this); @@ -45,9 +48,10 @@ public Boolean evaluate(ValueReferenceResolver valueRefResolver) { for (int i = 0; i < len; i++) { valueRefResolver.addExtension( ValueReferences.ITERATOR_EXTENSION_NAME, CapturedValue.of(collection.get(i))); - if (filterPredicateExpression.evaluate(valueRefResolver)) { + if (filterPredicateExpression.evaluate(evalContext)) { return Boolean.TRUE; } + checkTimeout(evalContext.getTimeoutChecker(), this); } return Boolean.FALSE; @@ -72,9 +76,10 @@ public Boolean evaluate(ValueReferenceResolver valueRefResolver) { valueRefResolver.addExtension( ValueReferences.ITERATOR_EXTENSION_NAME, CapturedValue.of(new MapValue.Entry(key, val))); - if (filterPredicateExpression.evaluate(valueRefResolver)) { + if (filterPredicateExpression.evaluate(evalContext)) { return Boolean.TRUE; } + checkTimeout(evalContext.getTimeoutChecker(), this); } return Boolean.FALSE; } catch (IllegalArgumentException | UnsupportedOperationException ex) { @@ -95,9 +100,10 @@ public Boolean evaluate(ValueReferenceResolver valueRefResolver) { for (Object val : setHolder) { valueRefResolver.addExtension( ValueReferences.ITERATOR_EXTENSION_NAME, CapturedValue.of(val)); - if (filterPredicateExpression.evaluate(valueRefResolver)) { + if (filterPredicateExpression.evaluate(evalContext)) { return Boolean.TRUE; } + checkTimeout(evalContext.getTimeoutChecker(), this); } return Boolean.FALSE; } catch (IllegalArgumentException | UnsupportedOperationException ex) { diff --git a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/IfElseExpression.java b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/IfElseExpression.java index cdd18689b11..1914b3a9a47 100644 --- a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/IfElseExpression.java +++ b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/IfElseExpression.java @@ -1,8 +1,10 @@ package com.datadog.debugger.el.expressions; +import static com.datadog.debugger.el.expressions.ExpressionHelper.checkTimeout; + +import com.datadog.debugger.el.EvalContext; import com.datadog.debugger.el.Expression; import com.datadog.debugger.el.Visitor; -import datadog.trace.bootstrap.debugger.el.ValueReferenceResolver; /** TODO: Primordial support for 'debugger watches' support */ public final class IfElseExpression implements Expression { @@ -18,12 +20,13 @@ public IfElseExpression( } @Override - public Void evaluate(ValueReferenceResolver valueRefResolver) { - if (test.evaluate(valueRefResolver)) { - thenExpression.evaluate(valueRefResolver); + public Void evaluate(EvalContext evalContext) { + if (test.evaluate(evalContext)) { + thenExpression.evaluate(evalContext); } else { - elseExpression.evaluate(valueRefResolver); + elseExpression.evaluate(evalContext); } + checkTimeout(evalContext.getTimeoutChecker(), this); return null; } diff --git a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/IfExpression.java b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/IfExpression.java index d799b01c3b5..cce1e53aedd 100644 --- a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/IfExpression.java +++ b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/IfExpression.java @@ -1,8 +1,10 @@ package com.datadog.debugger.el.expressions; +import static com.datadog.debugger.el.expressions.ExpressionHelper.checkTimeout; + +import com.datadog.debugger.el.EvalContext; import com.datadog.debugger.el.Expression; import com.datadog.debugger.el.Visitor; -import datadog.trace.bootstrap.debugger.el.ValueReferenceResolver; /** TODO: Primordial support for 'debugger watches' support */ public final class IfExpression implements Expression { @@ -15,10 +17,11 @@ public IfExpression(BooleanExpression test, Expression expression) { } @Override - public Void evaluate(ValueReferenceResolver valueRefResolver) { - if (test.evaluate(valueRefResolver)) { - expression.evaluate(valueRefResolver); + public Void evaluate(EvalContext evalContext) { + if (test.evaluate(evalContext)) { + expression.evaluate(evalContext); } + checkTimeout(evalContext.getTimeoutChecker(), this); return null; } diff --git a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/IndexExpression.java b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/IndexExpression.java index 9992286b0fa..8c718eb282d 100644 --- a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/IndexExpression.java +++ b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/IndexExpression.java @@ -2,14 +2,15 @@ import static com.datadog.debugger.el.expressions.CollectionExpressionHelper.checkSupportedList; import static com.datadog.debugger.el.expressions.CollectionExpressionHelper.checkSupportedMap; +import static com.datadog.debugger.el.expressions.ExpressionHelper.checkTimeout; +import com.datadog.debugger.el.EvalContext; import com.datadog.debugger.el.EvaluationException; import com.datadog.debugger.el.PrettyPrintVisitor; import com.datadog.debugger.el.Value; import com.datadog.debugger.el.Visitor; import com.datadog.debugger.el.values.ListValue; import com.datadog.debugger.el.values.MapValue; -import datadog.trace.bootstrap.debugger.el.ValueReferenceResolver; import datadog.trace.bootstrap.debugger.util.Redaction; public class IndexExpression implements ValueExpression> { @@ -23,8 +24,8 @@ public IndexExpression(ValueExpression target, ValueExpression key) { } @Override - public Value evaluate(ValueReferenceResolver valueRefResolver) { - Value targetValue = target.evaluate(valueRefResolver); + public Value evaluate(EvalContext evalContext) { + Value targetValue = target.evaluate(evalContext); if (targetValue.isUndefined()) { throw new EvaluationException( "Cannot evaluate the expression for undefined value", PrettyPrintVisitor.print(this)); @@ -34,7 +35,7 @@ public Value evaluate(ValueReferenceResolver valueRefResolver) { "Cannot evaluate the expression for null value", PrettyPrintVisitor.print(this)); } Value result = Value.undefinedValue(); - Value keyValue = key.evaluate(valueRefResolver); + Value keyValue = key.evaluate(evalContext); if (keyValue == Value.undefined()) { return result; } @@ -65,6 +66,7 @@ public Value evaluate(ValueReferenceResolver valueRefResolver) { if (obj != null && Redaction.isRedactedType(obj.getClass().getTypeName())) { ExpressionHelper.throwRedactedException(this); } + checkTimeout(evalContext.getTimeoutChecker(), this); return Value.of(result.getValue(), result.getType()); } diff --git a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/IsDefinedExpression.java b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/IsDefinedExpression.java index 5bada175eb4..27da03e06ae 100644 --- a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/IsDefinedExpression.java +++ b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/IsDefinedExpression.java @@ -1,9 +1,11 @@ package com.datadog.debugger.el.expressions; +import static com.datadog.debugger.el.expressions.ExpressionHelper.checkTimeout; + +import com.datadog.debugger.el.EvalContext; import com.datadog.debugger.el.EvaluationException; import com.datadog.debugger.el.Value; import com.datadog.debugger.el.Visitor; -import datadog.trace.bootstrap.debugger.el.ValueReferenceResolver; /** Check whether a {@linkplain Value} was resolved correctly or symbol exists.
*/ public class IsDefinedExpression implements BooleanExpression { @@ -14,15 +16,17 @@ public IsDefinedExpression(ValueExpression valueExpression) { } @Override - public Boolean evaluate(ValueReferenceResolver valueRefResolver) { + public Boolean evaluate(EvalContext evalContext) { if (valueExpression == null) { return Boolean.FALSE; } try { - Value value = valueExpression.evaluate(valueRefResolver); + Value value = valueExpression.evaluate(evalContext); return value.isUndefined() ? Boolean.FALSE : Boolean.TRUE; } catch (EvaluationException ex) { return Boolean.FALSE; + } finally { + checkTimeout(evalContext.getTimeoutChecker(), this); } } diff --git a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/IsEmptyExpression.java b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/IsEmptyExpression.java index 066aee72abc..1c4b43eef51 100644 --- a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/IsEmptyExpression.java +++ b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/IsEmptyExpression.java @@ -1,12 +1,14 @@ package com.datadog.debugger.el.expressions; +import static com.datadog.debugger.el.expressions.ExpressionHelper.checkTimeout; + +import com.datadog.debugger.el.EvalContext; import com.datadog.debugger.el.EvaluationException; import com.datadog.debugger.el.PrettyPrintVisitor; import com.datadog.debugger.el.Value; import com.datadog.debugger.el.Visitor; import com.datadog.debugger.el.values.CollectionValue; import com.datadog.debugger.el.values.StringValue; -import datadog.trace.bootstrap.debugger.el.ValueReferenceResolver; /** * Checks whether a {@linkplain Value} is empty.
@@ -20,8 +22,8 @@ public IsEmptyExpression(ValueExpression valueExpression) { } @Override - public Boolean evaluate(ValueReferenceResolver valueRefResolver) { - Value value = valueExpression.evaluate(valueRefResolver); + public Boolean evaluate(EvalContext evalContext) { + Value value = valueExpression.evaluate(evalContext); if (value.isUndefined()) { throw new EvaluationException( "Cannot evaluate the expression for undefined value", PrettyPrintVisitor.print(this)); @@ -30,12 +32,14 @@ public Boolean evaluate(ValueReferenceResolver valueRefResolver) { throw new EvaluationException( "Cannot evaluate the expression for null value", PrettyPrintVisitor.print(this)); } + boolean result = false; if (value instanceof CollectionValue) { - return ((CollectionValue) value).isEmpty(); + result = ((CollectionValue) value).isEmpty(); } else if (value instanceof StringValue) { - return ((StringValue) value).isEmpty(); + result = ((StringValue) value).isEmpty(); } - return Boolean.FALSE; + checkTimeout(evalContext.getTimeoutChecker(), this); + return result; } @Override diff --git a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/LenExpression.java b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/LenExpression.java index aee603c4779..3033d693cf1 100644 --- a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/LenExpression.java +++ b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/LenExpression.java @@ -1,5 +1,8 @@ package com.datadog.debugger.el.expressions; +import static com.datadog.debugger.el.expressions.ExpressionHelper.checkTimeout; + +import com.datadog.debugger.el.EvalContext; import com.datadog.debugger.el.EvaluationException; import com.datadog.debugger.el.PrettyPrintVisitor; import com.datadog.debugger.el.Value; @@ -8,7 +11,6 @@ import com.datadog.debugger.el.values.CollectionValue; import com.datadog.debugger.el.values.NumericValue; import com.datadog.debugger.el.values.StringValue; -import datadog.trace.bootstrap.debugger.el.ValueReferenceResolver; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -28,23 +30,27 @@ public LenExpression(ValueExpression source) { } @Override - public Value evaluate(ValueReferenceResolver valueRefResolver) { - Value materialized = source == null ? Value.nullValue() : source.evaluate(valueRefResolver); + public Value evaluate(EvalContext evalContext) { + Value materialized = source == null ? Value.nullValue() : source.evaluate(evalContext); + Value result = Value.undefined(); try { if (materialized.isNull()) { throw new RuntimeException("Cannot evaluate the expression for null value"); } else if (materialized.isUndefined()) { throw new RuntimeException("Cannot evaluate the expression for undefined value"); } else if (materialized instanceof StringValue) { - return (NumericValue) Value.of(((StringValue) materialized).length(), ValueType.INT); + result = (NumericValue) Value.of(((StringValue) materialized).length(), ValueType.INT); } else if (materialized instanceof CollectionValue) { - return (NumericValue) Value.of(((CollectionValue) materialized).count(), ValueType.INT); + result = (NumericValue) Value.of(((CollectionValue) materialized).count(), ValueType.INT); + } else { + throw new RuntimeException( + "Cannot evaluate the expression for " + materialized.getClass().getTypeName()); } } catch (RuntimeException ex) { throw new EvaluationException(ex.getMessage(), PrettyPrintVisitor.print(this)); } - log.warn("Can not compute length for {}", materialized); - return Value.undefined(); + checkTimeout(evalContext.getTimeoutChecker(), this); + return result; } public ValueExpression getSource() { diff --git a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/NotExpression.java b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/NotExpression.java index 6c71a7cc240..5cf8fad7607 100644 --- a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/NotExpression.java +++ b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/NotExpression.java @@ -1,7 +1,9 @@ package com.datadog.debugger.el.expressions; +import static com.datadog.debugger.el.expressions.ExpressionHelper.checkTimeout; + +import com.datadog.debugger.el.EvalContext; import com.datadog.debugger.el.Visitor; -import datadog.trace.bootstrap.debugger.el.ValueReferenceResolver; /** Will negate the resolved {@linkplain BooleanExpression} */ public final class NotExpression implements BooleanExpression { @@ -12,8 +14,10 @@ public NotExpression(BooleanExpression predicate) { } @Override - public Boolean evaluate(ValueReferenceResolver valueRefResolver) { - return !predicate.evaluate(valueRefResolver); + public Boolean evaluate(EvalContext evalContext) { + boolean result = !predicate.evaluate(evalContext); + checkTimeout(evalContext.getTimeoutChecker(), this); + return result; } @Override diff --git a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/StringPredicateExpression.java b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/StringPredicateExpression.java index 05d59b50b3c..efb1a4f3269 100644 --- a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/StringPredicateExpression.java +++ b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/StringPredicateExpression.java @@ -1,10 +1,10 @@ package com.datadog.debugger.el.expressions; +import com.datadog.debugger.el.EvalContext; import com.datadog.debugger.el.EvaluationException; import com.datadog.debugger.el.PrettyPrintVisitor; import com.datadog.debugger.el.Value; import com.datadog.debugger.el.values.StringValue; -import datadog.trace.bootstrap.debugger.el.ValueReferenceResolver; import java.util.function.BiPredicate; public class StringPredicateExpression implements BooleanExpression { @@ -25,9 +25,9 @@ public StringPredicateExpression( } @Override - public Boolean evaluate(ValueReferenceResolver valueRefResolver) { + public Boolean evaluate(EvalContext evalContext) { Value sourceValue = - sourceString != null ? sourceString.evaluate(valueRefResolver) : Value.nullValue(); + sourceString != null ? sourceString.evaluate(evalContext) : Value.nullValue(); if (sourceValue.isUndefined()) { throw new EvaluationException( "Cannot evaluate the expression for undefined value", PrettyPrintVisitor.print(this)); @@ -38,7 +38,9 @@ public Boolean evaluate(ValueReferenceResolver valueRefResolver) { } if (sourceValue.getValue() instanceof String) { String sourceStr = (String) sourceValue.getValue(); - return predicate.test(sourceStr, str.getValue()); + boolean result = predicate.test(sourceStr, str.getValue()); + ExpressionHelper.checkTimeout(evalContext.getTimeoutChecker(), this); + return result; } return Boolean.FALSE; } diff --git a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/SubStringExpression.java b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/SubStringExpression.java index c50e01271c9..40e7d258e33 100644 --- a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/SubStringExpression.java +++ b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/SubStringExpression.java @@ -1,11 +1,13 @@ package com.datadog.debugger.el.expressions; +import static com.datadog.debugger.el.expressions.ExpressionHelper.checkTimeout; + +import com.datadog.debugger.el.EvalContext; import com.datadog.debugger.el.EvaluationException; import com.datadog.debugger.el.PrettyPrintVisitor; import com.datadog.debugger.el.Value; import com.datadog.debugger.el.ValueType; import com.datadog.debugger.el.Visitor; -import datadog.trace.bootstrap.debugger.el.ValueReferenceResolver; public class SubStringExpression implements ValueExpression> { private final ValueExpression source; @@ -19,8 +21,8 @@ public SubStringExpression(ValueExpression source, int startIndex, int endInd } @Override - public Value evaluate(ValueReferenceResolver valueRefResolver) { - Value sourceValue = source != null ? source.evaluate(valueRefResolver) : Value.nullValue(); + public Value evaluate(EvalContext evalContext) { + Value sourceValue = source != null ? source.evaluate(evalContext) : Value.nullValue(); if (sourceValue.isUndefined()) { throw new EvaluationException( "Cannot evaluate the expression for undefined value", PrettyPrintVisitor.print(this)); @@ -31,7 +33,9 @@ public Value evaluate(ValueReferenceResolver valueRefResolver) { } if (sourceValue.getValue() instanceof String) { String sourceStr = (String) sourceValue.getValue(); - return internalEvaluate(sourceStr); + Value result = internalEvaluate(sourceStr); + checkTimeout(evalContext.getTimeoutChecker(), this); + return result; } return Value.undefined(); } diff --git a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/ThenExpression.java b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/ThenExpression.java index 024109a5f58..e75b02fc701 100644 --- a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/ThenExpression.java +++ b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/ThenExpression.java @@ -1,12 +1,12 @@ package com.datadog.debugger.el.expressions; +import com.datadog.debugger.el.EvalContext; import com.datadog.debugger.el.Value; -import datadog.trace.bootstrap.debugger.el.ValueReferenceResolver; /** TODO: Primordial support for 'debugger watches' support */ public final class ThenExpression implements ValueExpression> { @Override - public Value evaluate(ValueReferenceResolver valueRefResolver) { + public Value evaluate(EvalContext evalContext) { // TODO: This can be used to implement 'add watch' functionality where the script can amend the // collected snapshot return null; diff --git a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/ValueExpression.java b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/ValueExpression.java index 22d8e782a7d..5c43935555c 100644 --- a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/ValueExpression.java +++ b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/ValueExpression.java @@ -1,8 +1,8 @@ package com.datadog.debugger.el.expressions; +import com.datadog.debugger.el.EvalContext; import com.datadog.debugger.el.Expression; import com.datadog.debugger.el.Value; -import datadog.trace.bootstrap.debugger.el.ValueReferenceResolver; /** * A generic interface for expressions resolving to {@linkplain Value} @@ -13,7 +13,7 @@ public interface ValueExpression> extends Expression { ValueExpression NULL = new ValueExpression>() { @Override - public Value evaluate(ValueReferenceResolver valueRefResolver) { + public Value evaluate(EvalContext evalContext) { return Value.nullValue(); } @@ -26,7 +26,7 @@ public String toString() { ValueExpression UNDEFINED = new ValueExpression>() { @Override - public Value evaluate(ValueReferenceResolver valueRefResolver) { + public Value evaluate(EvalContext evalContext) { return Value.undefinedValue(); } diff --git a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/ValueRefExpression.java b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/ValueRefExpression.java index fa36ef9f15f..1b14838e167 100644 --- a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/ValueRefExpression.java +++ b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/ValueRefExpression.java @@ -1,7 +1,10 @@ package com.datadog.debugger.el.expressions; +import static com.datadog.debugger.el.expressions.ExpressionHelper.checkTimeout; +import static com.datadog.debugger.el.expressions.ExpressionHelper.throwRedactedException; import static datadog.trace.bootstrap.debugger.util.Redaction.REDACTED_VALUE; +import com.datadog.debugger.el.EvalContext; import com.datadog.debugger.el.EvaluationException; import com.datadog.debugger.el.Generated; import com.datadog.debugger.el.PrettyPrintVisitor; @@ -9,7 +12,6 @@ import com.datadog.debugger.el.ValueType; import com.datadog.debugger.el.Visitor; import datadog.trace.bootstrap.debugger.CapturedContext; -import datadog.trace.bootstrap.debugger.el.ValueReferenceResolver; import datadog.trace.bootstrap.debugger.util.Redaction; import java.util.Objects; @@ -22,18 +24,19 @@ public ValueRefExpression(String symbolName) { } @Override - public Value evaluate(ValueReferenceResolver valueRefResolver) { + public Value evaluate(EvalContext evalContext) { CapturedContext.CapturedValue symbol; try { - symbol = valueRefResolver.lookup(symbolName); + symbol = evalContext.getValueRefResolver().lookup(symbolName); } catch (RuntimeException ex) { throw new EvaluationException(ex.getMessage(), PrettyPrintVisitor.print(this)); } if (symbol != null) { String typeName = symbol.getType(); if (symbol.getValue() == REDACTED_VALUE || (Redaction.isRedactedType(typeName))) { - ExpressionHelper.throwRedactedException(this); + throwRedactedException(this); } + checkTimeout(evalContext.getTimeoutChecker(), this); return Value.of(symbol.getValue(), ValueType.of(symbol.getType())); } return Value.nullValue(); diff --git a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/WhenExpression.java b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/WhenExpression.java index 8ad9b0ad491..60f0b3cfb77 100644 --- a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/WhenExpression.java +++ b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/WhenExpression.java @@ -1,7 +1,7 @@ package com.datadog.debugger.el.expressions; +import com.datadog.debugger.el.EvalContext; import com.datadog.debugger.el.Visitor; -import datadog.trace.bootstrap.debugger.el.ValueReferenceResolver; /** The entry-point expression for the debugger EL */ public final class WhenExpression implements BooleanExpression { @@ -12,8 +12,8 @@ public WhenExpression(BooleanExpression expression) { } @Override - public Boolean evaluate(ValueReferenceResolver valueRefResolver) { - return expression.evaluate(valueRefResolver); + public Boolean evaluate(EvalContext evalContext) { + return expression.evaluate(evalContext); } @Override diff --git a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/values/ListValue.java b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/values/ListValue.java index d0c3dcffc63..8c9ce5fbb87 100644 --- a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/values/ListValue.java +++ b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/values/ListValue.java @@ -1,10 +1,13 @@ package com.datadog.debugger.el.values; +import static com.datadog.debugger.el.expressions.ExpressionHelper.checkArrayLength; +import static com.datadog.debugger.el.expressions.ExpressionHelper.checkCollectionSize; + +import com.datadog.debugger.el.EvalContext; import com.datadog.debugger.el.Value; import com.datadog.debugger.el.ValueType; import com.datadog.debugger.el.Visitor; import com.datadog.debugger.el.expressions.ValueExpression; -import datadog.trace.bootstrap.debugger.el.ValueReferenceResolver; import datadog.trace.bootstrap.debugger.el.Values; import datadog.trace.bootstrap.debugger.util.WellKnownClasses; import java.lang.reflect.Array; @@ -134,14 +137,17 @@ public Value get(int index) { @Override public boolean contains(Value val) { if (listHolder instanceof Collection) { - if (WellKnownClasses.isSafe((Collection) listHolder)) { - return ((Collection) listHolder).contains(val.isNull() ? null : val.getValue()); + Collection collection = (Collection) listHolder; + if (WellKnownClasses.isSafe(collection)) { + checkCollectionSize(collection, this); + return collection.contains(val.isNull() ? null : val.getValue()); } throw new UnsupportedOperationException( "Unsupported Collection class: " + listHolder.getClass().getTypeName()); } if (arrayHolder != null) { int count = Array.getLength(arrayHolder); + checkArrayLength(count, this); if (arrayType.isPrimitive()) { if (val.getValue() == null || val.isNull()) { throw new IllegalArgumentException("Cannot compare null with primitive array"); @@ -255,7 +261,7 @@ public Object getValue() { } @Override - public ListValue evaluate(ValueReferenceResolver valueRefResolver) { + public ListValue evaluate(EvalContext evalContext) { return this; } diff --git a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/values/MapValue.java b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/values/MapValue.java index c06680aa56e..05778531c82 100644 --- a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/values/MapValue.java +++ b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/values/MapValue.java @@ -1,10 +1,10 @@ package com.datadog.debugger.el.values; +import com.datadog.debugger.el.EvalContext; import com.datadog.debugger.el.Value; import com.datadog.debugger.el.ValueType; import com.datadog.debugger.el.Visitor; import com.datadog.debugger.el.expressions.ValueExpression; -import datadog.trace.bootstrap.debugger.el.ValueReferenceResolver; import datadog.trace.bootstrap.debugger.el.Values; import datadog.trace.bootstrap.debugger.util.WellKnownClasses; import java.util.Collections; @@ -144,7 +144,7 @@ public Object getValue() { } @Override - public MapValue evaluate(ValueReferenceResolver valueRefResolver) { + public MapValue evaluate(EvalContext evalContext) { return this; } diff --git a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/values/SetValue.java b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/values/SetValue.java index 07651471858..9654c1b99c8 100644 --- a/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/values/SetValue.java +++ b/dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/values/SetValue.java @@ -1,10 +1,10 @@ package com.datadog.debugger.el.values; +import com.datadog.debugger.el.EvalContext; import com.datadog.debugger.el.Value; import com.datadog.debugger.el.ValueType; import com.datadog.debugger.el.Visitor; import com.datadog.debugger.el.expressions.ValueExpression; -import datadog.trace.bootstrap.debugger.el.ValueReferenceResolver; import datadog.trace.bootstrap.debugger.el.Values; import datadog.trace.bootstrap.debugger.util.WellKnownClasses; import java.util.Collection; @@ -25,7 +25,7 @@ public SetValue(Object object) { } @Override - public SetValue evaluate(ValueReferenceResolver valueRefResolver) { + public SetValue evaluate(EvalContext evalContext) { return this; } diff --git a/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/BooleanValueExpressionAdapterTest.java b/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/BooleanValueExpressionAdapterTest.java index 3b793ecb1d6..01a09dd6569 100644 --- a/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/BooleanValueExpressionAdapterTest.java +++ b/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/BooleanValueExpressionAdapterTest.java @@ -1,10 +1,10 @@ package com.datadog.debugger.el; +import static com.datadog.debugger.el.EvalContextHelper.createEvalContext; import static org.junit.jupiter.api.Assertions.*; import com.datadog.debugger.el.expressions.BooleanExpression; import com.datadog.debugger.el.values.BooleanValue; -import datadog.trace.bootstrap.debugger.el.ValueReferenceResolver; import org.junit.jupiter.api.Test; class BooleanValueExpressionAdapterTest { @@ -29,20 +29,14 @@ public void testLiteral() { public void testExpression() { BooleanValueExpressionAdapter booleanValueExpressionAdapter = new BooleanValueExpressionAdapter(DSL.eq(DSL.value(1), DSL.value(1))); - BooleanValue resultValue = booleanValueExpressionAdapter.evaluate(null); + BooleanValue resultValue = booleanValueExpressionAdapter.evaluate(createEvalContext(this)); assertTrue(resultValue.getValue()); } @Test public void testNull() { BooleanValueExpressionAdapter booleanValueExpressionAdapter = - new BooleanValueExpressionAdapter( - new BooleanExpression() { - @Override - public Boolean evaluate(ValueReferenceResolver valueRefResolver) { - return null; - } - }); + new BooleanValueExpressionAdapter(evalContext -> null); EvaluationException ex = assertThrows(EvaluationException.class, () -> booleanValueExpressionAdapter.evaluate(null)); assertEquals("Boolean expression returning null", ex.getMessage()); diff --git a/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/RefResolverHelper.java b/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/EvalContextHelper.java similarity index 72% rename from dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/RefResolverHelper.java rename to dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/EvalContextHelper.java index 88b33231c3f..9df06537c7e 100644 --- a/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/RefResolverHelper.java +++ b/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/EvalContextHelper.java @@ -1,11 +1,27 @@ package com.datadog.debugger.el; +import datadog.trace.api.Config; import datadog.trace.bootstrap.debugger.CapturedContext; import datadog.trace.bootstrap.debugger.el.ValueReferenceResolver; import datadog.trace.bootstrap.debugger.util.Redaction; +import datadog.trace.bootstrap.debugger.util.TimeoutChecker; +import java.time.Duration; import java.util.*; -public class RefResolverHelper { +public class EvalContextHelper { + + public static final Duration TEST_TIMEOUT = Duration.ofMillis(1000); + + public static EvalContext createEvalContext(Object instance) { + // create a higher timeout for test to avoid flakiness + return new EvalContext( + createResolver(instance), TimeoutChecker.create(Config.get(), TEST_TIMEOUT)); + } + + // specify lower timeout to test timeout checker + public static EvalContext createEvalContext(Object instance, Duration timeout) { + return new EvalContext(createResolver(instance), TimeoutChecker.create(Config.get(), timeout)); + } public static ValueReferenceResolver createResolver(Object instance) { CapturedContext.CapturedValue thisValue = diff --git a/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/ExpressionTest.java b/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/ExpressionTest.java index f88cf81e472..de75a00713f 100644 --- a/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/ExpressionTest.java +++ b/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/ExpressionTest.java @@ -1,6 +1,7 @@ package com.datadog.debugger.el; import static com.datadog.debugger.el.DSL.*; +import static com.datadog.debugger.el.EvalContextHelper.createEvalContext; import static com.datadog.debugger.el.PrettyPrintVisitor.print; import static org.junit.jupiter.api.Assertions.*; @@ -9,7 +10,6 @@ import com.datadog.debugger.el.values.NumericValue; import com.datadog.debugger.el.values.StringValue; import datadog.trace.bootstrap.debugger.CapturedContext; -import datadog.trace.bootstrap.debugger.el.ValueReferenceResolver; import java.util.stream.Stream; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -21,7 +21,7 @@ class ExpressionTest { @MethodSource("literalExpressions") void testLiteralExpressions(Literal literal, Object expectedValue) { Value value1 = literal.evaluate(null); - Value value2 = literal.evaluate(new CapturedContext()); + Value value2 = literal.evaluate(new EvalContext(new CapturedContext(), null)); assertNotNull(value1); assertNotNull(value2); @@ -43,16 +43,16 @@ void testPredicateExpression() { StringValue string = new StringValue("Hello World"); StringValue emptyString = new StringValue(""); - ValueReferenceResolver resolver = new CapturedContext(); + EvalContext evalContext = createEvalContext(this); IsEmptyExpression isEmpty1 = new IsEmptyExpression(string); IsEmptyExpression isEmpty2 = new IsEmptyExpression(emptyString); - assertFalse(isEmpty1.evaluate(resolver)); - assertTrue(isEmpty2.evaluate(resolver)); + assertFalse(isEmpty1.evaluate(evalContext)); + assertTrue(isEmpty2.evaluate(evalContext)); - assertTrue(not(isEmpty1).evaluate(resolver)); - assertTrue(or(isEmpty1, isEmpty2).evaluate(resolver)); - assertFalse(and(isEmpty1, isEmpty2).evaluate(resolver)); + assertTrue(not(isEmpty1).evaluate(evalContext)); + assertTrue(or(isEmpty1, isEmpty2).evaluate(evalContext)); + assertFalse(and(isEmpty1, isEmpty2).evaluate(evalContext)); } @Test diff --git a/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/ProbeConditionTest.java b/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/ProbeConditionTest.java index 8d6f8a3eae3..50ba2590b2a 100644 --- a/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/ProbeConditionTest.java +++ b/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/ProbeConditionTest.java @@ -10,7 +10,11 @@ import com.squareup.moshi.JsonAdapter; import com.squareup.moshi.Moshi; +import datadog.trace.api.Config; +import datadog.trace.bootstrap.debugger.el.ReflectiveFieldValueResolver; import datadog.trace.bootstrap.debugger.el.ValueReferenceResolver; +import datadog.trace.bootstrap.debugger.util.Redaction; +import datadog.trace.bootstrap.debugger.util.TimeoutChecker; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; @@ -28,12 +32,22 @@ import java.util.UUID; import java.util.stream.Collectors; import okio.Okio; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; public class ProbeConditionTest { + private static final Duration TEST_TIMEOUT = Duration.ofMillis(500); // used in testExecuteCondition private int field = 10; + @BeforeAll + static void beforeAll() { + // Warming up here for first call, to avoid reaching timeout when evaluating + Redaction.isRedactedKeyword("strList"); + ReflectiveFieldValueResolver.getFieldAsCapturedValue( + ProbeConditionTest.class, new ProbeConditionTest(), "field"); + } + @Test void testExecuteCondition() throws Exception { ProbeCondition probeCondition = loadFromResource("/test_conditional_01.json"); @@ -42,17 +56,17 @@ class Obj1 { private int field = 10; List field2 = new ArrayList<>(); } - ValueReferenceResolver ctx = RefResolverHelper.createResolver(new Obj1()); + ValueReferenceResolver ctx = EvalContextHelper.createResolver(new Obj1()); - assertTrue(probeCondition.execute(ctx)); + assertTrue(probeCondition.execute(ctx, TimeoutChecker.create(Config.get(), TEST_TIMEOUT))); class Obj2 { Collection tags = Arrays.asList("hey", "world", "ko"); private int field = 10; List field2 = new ArrayList<>(); } - ValueReferenceResolver ctx2 = RefResolverHelper.createResolver(new Obj2()); - assertFalse(probeCondition.execute(ctx2)); + ValueReferenceResolver ctx2 = EvalContextHelper.createResolver(new Obj2()); + assertFalse(probeCondition.execute(ctx2, TimeoutChecker.create(Config.get(), TEST_TIMEOUT))); } @Test @@ -62,18 +76,20 @@ class Obj { Container container = new Container("hello"); } ValueReferenceResolver ctx = - RefResolverHelper.createResolver( + EvalContextHelper.createResolver( singletonMap("this", new Obj()), singletonMap("container", new Container("world"))); - assertTrue(probeCondition.execute(ctx)); + assertTrue(probeCondition.execute(ctx, TimeoutChecker.create(Config.get(), TEST_TIMEOUT))); class Obj2 { Container obj = new Container("hello"); } ValueReferenceResolver ctx2 = - RefResolverHelper.createResolver( + EvalContextHelper.createResolver( singletonMap("this", new Obj2()), singletonMap("container", new Container("world"))); RuntimeException runtimeException = - assertThrows(RuntimeException.class, () -> probeCondition.execute(ctx2)); + assertThrows( + RuntimeException.class, + () -> probeCondition.execute(ctx2, TimeoutChecker.create(Config.get(), TEST_TIMEOUT))); assertEquals("Cannot dereference field: container", runtimeException.getMessage()); } @@ -84,8 +100,8 @@ class Obj { int intField1 = 42; String strField = "foo"; } - ValueReferenceResolver ctx = RefResolverHelper.createResolver(new Obj()); - assertTrue(probeCondition.execute(ctx)); + ValueReferenceResolver ctx = EvalContextHelper.createResolver(new Obj()); + assertTrue(probeCondition.execute(ctx, TimeoutChecker.create(Config.get(), TEST_TIMEOUT))); } @Test @@ -95,9 +111,9 @@ class Obj { Object objField = new Object(); } ValueReferenceResolver ctx = - RefResolverHelper.createResolver( + EvalContextHelper.createResolver( singletonMap("this", new Obj()), singletonMap("nullField", null)); - assertTrue(probeCondition.execute(ctx)); + assertTrue(probeCondition.execute(ctx, TimeoutChecker.create(Config.get(), TEST_TIMEOUT))); } @Test @@ -116,8 +132,8 @@ class Obj { int idx = 1; } - ValueReferenceResolver ctx = RefResolverHelper.createResolver(new Obj()); - assertTrue(probeCondition.execute(ctx)); + ValueReferenceResolver ctx = EvalContextHelper.createResolver(new Obj()); + assertTrue(probeCondition.execute(ctx, TimeoutChecker.create(Config.get(), TEST_TIMEOUT))); } @Test @@ -126,8 +142,8 @@ void testStringOperation() throws Exception { class Obj { String strField = "foobar"; } - ValueReferenceResolver ctx = RefResolverHelper.createResolver(new Obj()); - assertTrue(probeCondition.execute(ctx)); + ValueReferenceResolver ctx = EvalContextHelper.createResolver(new Obj()); + assertTrue(probeCondition.execute(ctx, TimeoutChecker.create(Config.get(), TEST_TIMEOUT))); } @Test @@ -148,10 +164,10 @@ void testJsonParsing() throws IOException { class Obj { Collection vets = Arrays.asList("vet1", "vet2", "vet3"); } - ValueReferenceResolver ctx = RefResolverHelper.createResolver(new Obj()); + ValueReferenceResolver ctx = EvalContextHelper.createResolver(new Obj()); // the condition checks if length of vets > 2 - assertTrue(probeCondition.execute(ctx)); + assertTrue(probeCondition.execute(ctx, TimeoutChecker.create(Config.get(), TEST_TIMEOUT))); } @Test @@ -191,9 +207,11 @@ void redaction() throws IOException { ProbeCondition probeCondition = loadFromResource("/test_conditional_09.json"); Map args = new HashMap<>(); args.put("password", "secret123"); - ValueReferenceResolver ctx = RefResolverHelper.createResolver(args, null); + ValueReferenceResolver ctx = EvalContextHelper.createResolver(args, null); EvaluationException evaluationException = - assertThrows(EvaluationException.class, () -> probeCondition.execute(ctx)); + assertThrows( + EvaluationException.class, + () -> probeCondition.execute(ctx, TimeoutChecker.create(Config.get(), TEST_TIMEOUT))); assertEquals( "Could not evaluate the expression because 'password' was redacted", evaluationException.getMessage()); @@ -207,8 +225,8 @@ void primitives() throws IOException { args.put("duration", Duration.ofSeconds(42)); args.put("clazz", "foo".getClass()); args.put("now", new Date(1700000000000L)); // 2023-11-14T00:00:00Z - ValueReferenceResolver ctx = RefResolverHelper.createResolver(args, null); - assertTrue(probeCondition.execute(ctx)); + ValueReferenceResolver ctx = EvalContextHelper.createResolver(args, null); + assertTrue(probeCondition.execute(ctx, TimeoutChecker.create(Config.get(), TEST_TIMEOUT))); } @Test @@ -222,8 +240,8 @@ class Obj { Set emptySet = new HashSet<>(); Object[] emptyArray = new Object[0]; } - ValueReferenceResolver ctx = RefResolverHelper.createResolver(new Obj()); - assertTrue(probeCondition.execute(ctx)); + ValueReferenceResolver ctx = EvalContextHelper.createResolver(new Obj()); + assertTrue(probeCondition.execute(ctx, TimeoutChecker.create(Config.get(), TEST_TIMEOUT))); } @Test @@ -238,16 +256,24 @@ class Obj { Object objVal = null; char charVal = 'a'; } - ValueReferenceResolver ctx = RefResolverHelper.createResolver(new Obj()); - assertTrue(probeCondition.execute(ctx)); + ValueReferenceResolver ctx = EvalContextHelper.createResolver(new Obj()); + assertTrue(probeCondition.execute(ctx, TimeoutChecker.create(Config.get(), TEST_TIMEOUT))); } @Test - void testLenCount() throws Exception { + void testTimeoutAndLenCount() throws Exception { ProbeCondition probeCondition = loadFromResource("/test_conditional_14.json"); class Obj { int[] intArray = new int[] {1, 1, 1}; String[] strArray = new String[] {"foo", "bar"}; + List largeList = new ArrayList<>(); + + { + for (int i = 0; i < 1_000; i++) { + largeList.add("foobar" + i); + } + } + Map strMap = new HashMap<>(); { @@ -267,8 +293,18 @@ class Obj { strList.add("foo"); } } - ValueReferenceResolver ctx = RefResolverHelper.createResolver(new Obj()); - assertTrue(probeCondition.execute(ctx)); + Obj obj = new Obj(); + ValueReferenceResolver ctx = EvalContextHelper.createResolver(obj); + // first call is longer so ideal to test timeout + EvaluationException evaluationException = + assertThrows( + EvaluationException.class, + () -> + probeCondition.execute( + ctx, TimeoutChecker.create(Config.get(), Duration.ofMillis(1)))); + assertEquals("timeout (1ms)", evaluationException.getMessage()); + // test good execution + assertTrue(probeCondition.execute(ctx, TimeoutChecker.create(Config.get(), TEST_TIMEOUT))); } @Test @@ -278,9 +314,11 @@ class Obj { } List lines = loadLinesFromResource("/null_expressions.txt"); for (String line : lines) { - ValueReferenceResolver ctx = RefResolverHelper.createResolver(new Obj()); + ValueReferenceResolver ctx = EvalContextHelper.createResolver(new Obj()); EvaluationException ex = - assertThrows(EvaluationException.class, () -> load(line).execute(ctx)); + assertThrows( + EvaluationException.class, + () -> load(line).execute(ctx, TimeoutChecker.create(Config.get(), TEST_TIMEOUT))); assertEquals("Cannot evaluate the expression for null value", ex.getMessage(), line); } } @@ -299,11 +337,14 @@ class Obj { } List lines = loadLinesFromResource("/contains_expressions.txt"); for (String line : lines) { - ValueReferenceResolver ctx = RefResolverHelper.createResolver(new Obj()); - assertTrue(load(line).execute(ctx)); + ValueReferenceResolver ctx = EvalContextHelper.createResolver(new Obj()); + assertTrue(load(line).execute(ctx, TimeoutChecker.create(Config.get(), TEST_TIMEOUT))); } } + @Test + public void timeoutExpressions() {} + private static ProbeCondition loadFromResource(String resourcePath) throws IOException { InputStream input = ProbeConditionTest.class.getResourceAsStream(resourcePath); Moshi moshi = diff --git a/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/ValueScriptTest.java b/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/ValueScriptTest.java index eefbca9956a..fccf05ffeb7 100644 --- a/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/ValueScriptTest.java +++ b/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/ValueScriptTest.java @@ -1,13 +1,19 @@ package com.datadog.debugger.el; +import static com.datadog.debugger.el.EvalContextHelper.createResolver; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; import com.squareup.moshi.Moshi; +import datadog.trace.api.Config; import datadog.trace.bootstrap.debugger.el.Values; +import datadog.trace.bootstrap.debugger.util.TimeoutChecker; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.time.Duration; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; @@ -15,22 +21,43 @@ import org.junit.jupiter.api.Test; public class ValueScriptTest { + private static final Duration TEST_TIMEOUT = Duration.ofMinutes(500); static class Obj { String str = "hello"; int i = 10; List list = Arrays.asList("a", "b", "c"); + List largeList = new ArrayList<>(); long l = 100_000_000_000L; float f = 2.5F; double d = 3.14D; char c = 'a'; + + { + for (int i = 0; i < 5_000; i++) { + largeList.add("hello" + i); + } + } } @Test public void predicates() { ValueScript valueScript = loadFromResource("/test_value_expr_01.json"); + // first call is longer so ideal to test timeout + EvaluationException evaluationException = + assertThrows( + EvaluationException.class, + () -> + valueScript.execute( + createResolver(new Obj()), + TimeoutChecker.create(Config.get(), Duration.ofMillis(1)))); + assertEquals("timeout (1ms)", evaluationException.getMessage()); + // test good execution assertEquals( - Boolean.TRUE, valueScript.execute(RefResolverHelper.createResolver(new Obj())).getValue()); + Boolean.TRUE, + valueScript + .execute(createResolver(new Obj()), TimeoutChecker.create(Config.get(), TEST_TIMEOUT)) + .getValue()); } @Test @@ -40,7 +67,9 @@ public void topLevelPredicates() { ValueScript valueScript = load(line); assertEquals( Boolean.TRUE, - valueScript.execute(RefResolverHelper.createResolver(new Obj())).getValue(), + valueScript + .execute(createResolver(new Obj()), TimeoutChecker.create(Config.get(), TEST_TIMEOUT)) + .getValue(), line); } } @@ -77,7 +106,9 @@ public void topLevelPrimitives() { ValueScript valueScript = load(line); assertEquals( expectedValues[i], - valueScript.execute(RefResolverHelper.createResolver(new Obj())).getValue(), + valueScript + .execute(createResolver(new Obj()), TimeoutChecker.create(Config.get(), TEST_TIMEOUT)) + .getValue(), line); i++; } diff --git a/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/expressions/BinaryExpressionTest.java b/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/expressions/BinaryExpressionTest.java index 8faaf61d48a..ef6de51409f 100644 --- a/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/expressions/BinaryExpressionTest.java +++ b/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/expressions/BinaryExpressionTest.java @@ -1,20 +1,23 @@ package com.datadog.debugger.el.expressions; +import static com.datadog.debugger.el.EvalContextHelper.createEvalContext; import static com.datadog.debugger.el.PrettyPrintVisitor.print; 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 com.datadog.debugger.el.RefResolverHelper; +import com.datadog.debugger.el.EvalContext; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; class BinaryExpressionTest { + EvalContext evalContext = createEvalContext(this); + @Test void testLeftNull() { BinaryExpression expression = new BinaryExpression(null, BooleanExpression.TRUE, BinaryOperator.AND); - assertFalse(expression.evaluate(RefResolverHelper.createResolver(this))); + assertFalse(expression.evaluate(evalContext)); assertEquals("false && true", print(expression)); } @@ -22,7 +25,7 @@ void testLeftNull() { void testRightNull() { BinaryExpression expression = new BinaryExpression(BooleanExpression.TRUE, null, BinaryOperator.AND); - assertFalse(expression.evaluate(RefResolverHelper.createResolver(this))); + assertFalse(expression.evaluate(evalContext)); assertEquals("true && false", print(expression)); } @@ -33,7 +36,7 @@ void testShortCircuitAnd() { BooleanExpression.FALSE, valueRefResolver -> Assertions.fail("should not reach"), BinaryOperator.AND); - assertFalse(expression.evaluate(RefResolverHelper.createResolver(this))); + assertFalse(expression.evaluate(evalContext)); assertEquals("false && null", print(expression)); } @@ -44,7 +47,7 @@ void testShortCircuitOr() { BooleanExpression.TRUE, valueRefResolver -> Assertions.fail("should not reach"), BinaryOperator.OR); - assertTrue(expression.evaluate(RefResolverHelper.createResolver(this))); + assertTrue(expression.evaluate(evalContext)); assertEquals("true || null", print(expression)); } } diff --git a/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/expressions/ComparisonExpressionTest.java b/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/expressions/ComparisonExpressionTest.java index d2b2e1cd8c4..28ac3425f3e 100644 --- a/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/expressions/ComparisonExpressionTest.java +++ b/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/expressions/ComparisonExpressionTest.java @@ -1,5 +1,6 @@ package com.datadog.debugger.el.expressions; +import static com.datadog.debugger.el.EvalContextHelper.createEvalContext; import static com.datadog.debugger.el.PrettyPrintVisitor.print; import static com.datadog.debugger.el.ValueType.DOUBLE; import static com.datadog.debugger.el.ValueType.FLOAT; @@ -14,6 +15,7 @@ import static com.datadog.debugger.el.expressions.ComparisonOperator.LT; import static org.junit.jupiter.api.Assertions.*; +import com.datadog.debugger.el.EvalContext; import com.datadog.debugger.el.EvaluationException; import com.datadog.debugger.el.values.NumericValue; import com.datadog.debugger.el.values.ObjectValue; @@ -41,7 +43,7 @@ void evaluateOperator( boolean expected, String prettyPrint) { ComparisonExpression expression = new ComparisonExpression(left, right, operator); - assertEquals(expected, expression.evaluate(NoopResolver.INSTANCE)); + assertEquals(expected, expression.evaluate(createEvalContext(NoopResolver.INSTANCE))); assertEquals(prettyPrint, print(expression)); } @@ -243,7 +245,7 @@ void evaluateOperatorStrings( boolean expected, String prettyPrint) { ComparisonExpression expression = new ComparisonExpression(left, right, operator); - assertEquals(expected, expression.evaluate(NoopResolver.INSTANCE)); + assertEquals(expected, expression.evaluate(createEvalContext(NoopResolver.INSTANCE))); assertEquals(prettyPrint, print(expression)); } @@ -273,35 +275,35 @@ private static Stream expressionStrs() { void evaluateSecondUndefined() { ComparisonExpression expression = new ComparisonExpression(new NumericValue(1, INT), ValueExpression.UNDEFINED, EQ); - assertFalse(expression.evaluate(NoopResolver.INSTANCE)); + assertFalse(expression.evaluate(new EvalContext(NoopResolver.INSTANCE, null))); } @Test void evaluateBothUndefined() { ComparisonExpression expression = new ComparisonExpression(ValueExpression.UNDEFINED, ValueExpression.UNDEFINED, EQ); - assertFalse(expression.evaluate(NoopResolver.INSTANCE)); + assertFalse(expression.evaluate(new EvalContext(NoopResolver.INSTANCE, null))); } @Test void evaluateFirstNull() { ComparisonExpression expression = new ComparisonExpression(ValueExpression.NULL, new NumericValue(2, INT), EQ); - assertFalse(expression.evaluate(NoopResolver.INSTANCE)); + assertFalse(expression.evaluate(createEvalContext(NoopResolver.INSTANCE))); } @Test void evaluateSecondNull() { ComparisonExpression expression = new ComparisonExpression(new NumericValue(1, INT), ValueExpression.NULL, EQ); - assertFalse(expression.evaluate(NoopResolver.INSTANCE)); + assertFalse(expression.evaluate(createEvalContext(NoopResolver.INSTANCE))); } @Test void evaluateBothNull() { ComparisonExpression expression = new ComparisonExpression(ValueExpression.NULL, ValueExpression.NULL, EQ); - assertTrue(expression.evaluate(NoopResolver.INSTANCE)); + assertTrue(expression.evaluate(createEvalContext(NoopResolver.INSTANCE))); } @Test @@ -309,7 +311,9 @@ void invalidInstanceofOperand() { ComparisonExpression expression = new ComparisonExpression(new StringValue("foo"), new NumericValue(1, INT), INSTANCEOF); EvaluationException evaluationException = - assertThrows(EvaluationException.class, () -> expression.evaluate(NoopResolver.INSTANCE)); + assertThrows( + EvaluationException.class, + () -> expression.evaluate(createEvalContext(NoopResolver.INSTANCE))); assertEquals( "Right operand of instanceof operator must be a string literal", evaluationException.getMessage()); @@ -321,7 +325,9 @@ void invalidInstanceofClassName() { ComparisonExpression expression = new ComparisonExpression(new StringValue("foo"), new StringValue("String"), INSTANCEOF); EvaluationException evaluationException = - assertThrows(EvaluationException.class, () -> expression.evaluate(NoopResolver.INSTANCE)); + assertThrows( + EvaluationException.class, + () -> expression.evaluate(createEvalContext(NoopResolver.INSTANCE))); assertEquals("Class not found: String", evaluationException.getMessage()); assertEquals("\"foo\" instanceof \"String\"", evaluationException.getExpr()); } diff --git a/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/expressions/ContainsExpressionTest.java b/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/expressions/ContainsExpressionTest.java index 88b08a94c3f..60cd3d9f5c6 100644 --- a/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/expressions/ContainsExpressionTest.java +++ b/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/expressions/ContainsExpressionTest.java @@ -1,27 +1,46 @@ package com.datadog.debugger.el.expressions; +import static com.datadog.debugger.el.EvalContextHelper.createEvalContext; import static com.datadog.debugger.el.PrettyPrintVisitor.print; import static org.junit.jupiter.api.Assertions.*; import com.datadog.debugger.el.DSL; +import com.datadog.debugger.el.EvalContext; import com.datadog.debugger.el.EvaluationException; -import com.datadog.debugger.el.RefResolverHelper; import com.datadog.debugger.el.values.StringValue; -import datadog.trace.bootstrap.debugger.el.ValueReferenceResolver; import datadog.trace.bootstrap.debugger.el.Values; +import java.time.Duration; +import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.locks.LockSupport; import org.junit.jupiter.api.Test; class ContainsExpressionTest { - private final ValueReferenceResolver resolver = RefResolverHelper.createResolver(this); + private final EvalContext evalContext = createEvalContext(this); private List list = Arrays.asList("foo", "bar", "baz"); private List listWithNull = Arrays.asList("foo", null, "baz"); + private List largeList = new ArrayList<>(); + + { + for (int i = 0; i < 1_000_001; i++) { + largeList.add(i); + } + } + + private List slowList = new ArrayList<>(); + + { + for (int i = 0; i < 10_000; i++) { + slowList.add(String.valueOf(i)); + } + } + private String[] arrayStr = new String[] {"foo", "bar", "baz"}; private String[] arrayStrWithNull = new String[] {"foo", null, "bar"}; private int[] arrayInt = new int[] {1, 2, 3}; @@ -60,7 +79,7 @@ class ContainsExpressionTest { void nullExpression() { ContainsExpression expression = new ContainsExpression(null, null); EvaluationException exception = - assertThrows(EvaluationException.class, () -> expression.evaluate(resolver)); + assertThrows(EvaluationException.class, () -> expression.evaluate(evalContext)); assertEquals("Cannot evaluate the expression for null value", exception.getMessage()); assertEquals("contains(null, null)", print(expression)); } @@ -70,7 +89,7 @@ void undefinedExpression() { ContainsExpression expression = new ContainsExpression(DSL.value(Values.UNDEFINED_OBJECT), new StringValue(null)); EvaluationException exception = - assertThrows(EvaluationException.class, () -> expression.evaluate(resolver)); + assertThrows(EvaluationException.class, () -> expression.evaluate(evalContext)); assertEquals("Cannot evaluate the expression for undefined value", exception.getMessage()); assertEquals("contains(UNDEFINED, \"null\")", print(expression)); } @@ -79,16 +98,16 @@ void undefinedExpression() { void stringExpression() { ContainsExpression expression = new ContainsExpression(DSL.value("abcd"), new StringValue("bc")); - assertTrue(expression.evaluate(resolver)); + assertTrue(expression.evaluate(evalContext)); assertEquals("contains(\"abcd\", \"bc\")", print(expression)); expression = new ContainsExpression(DSL.value("abc"), new StringValue("dc")); - assertFalse(expression.evaluate(resolver)); + assertFalse(expression.evaluate(evalContext)); assertEquals("contains(\"abc\", \"dc\")", print(expression)); ContainsExpression nullValExpression = new ContainsExpression(DSL.value("abcd"), null); EvaluationException exception = - assertThrows(EvaluationException.class, () -> nullValExpression.evaluate(resolver)); + assertThrows(EvaluationException.class, () -> nullValExpression.evaluate(evalContext)); assertEquals("Cannot evaluate the expression for non-string value", exception.getMessage()); assertEquals("contains(\"abcd\", null)", print(nullValExpression)); } @@ -96,43 +115,55 @@ void stringExpression() { @Test void listExpression() { ContainsExpression expression = new ContainsExpression(DSL.ref("list"), DSL.value("bar")); - assertTrue(expression.evaluate(resolver)); + assertTrue(expression.evaluate(evalContext)); assertEquals("contains(list, \"bar\")", print(expression)); expression = new ContainsExpression(DSL.ref("listWithNull"), null); - assertTrue(expression.evaluate(resolver)); + assertTrue(expression.evaluate(evalContext)); assertEquals("contains(listWithNull, null)", print(expression)); + + ContainsExpression largeExpression = new ContainsExpression(DSL.ref("largeList"), DSL.value(1)); + EvaluationException evaluationException = + assertThrows(EvaluationException.class, () -> largeExpression.evaluate(evalContext)); + assertEquals("Collection too large (>1000000)", evaluationException.getMessage()); + + ContainsExpression slowListExpression = + new ContainsExpression(DSL.ref("slowList"), DSL.value(new SlowObject())); + assertThrows( + EvaluationException.class, + () -> slowListExpression.evaluate(createEvalContext(this, Duration.ofMillis(1)))); } @Test void arrayExpression() { ContainsExpression expression = new ContainsExpression(DSL.ref("arrayStr"), DSL.value("bar")); - assertTrue(expression.evaluate(resolver)); + assertTrue(expression.evaluate(evalContext)); assertEquals("contains(arrayStr, \"bar\")", print(expression)); expression = new ContainsExpression(DSL.ref("arrayInt"), DSL.value(2)); - assertTrue(expression.evaluate(resolver)); + assertTrue(expression.evaluate(evalContext)); assertEquals("contains(arrayInt, 2)", print(expression)); expression = new ContainsExpression(DSL.ref("arrayChar"), DSL.value("b")); - assertTrue(expression.evaluate(resolver)); + assertTrue(expression.evaluate(evalContext)); assertEquals("contains(arrayChar, \"b\")", print(expression)); expression = new ContainsExpression(DSL.ref("arrayLong"), DSL.value(2)); - assertTrue(expression.evaluate(resolver)); + assertTrue(expression.evaluate(evalContext)); assertEquals("contains(arrayLong, 2)", print(expression)); expression = new ContainsExpression(DSL.ref("arrayDouble"), DSL.value(2.0)); - assertTrue(expression.evaluate(resolver)); + assertTrue(expression.evaluate(evalContext)); assertEquals("contains(arrayDouble, 2.0)", print(expression)); expression = new ContainsExpression(DSL.ref("arrayStrWithNull"), null); - assertTrue(expression.evaluate(resolver)); + assertTrue(expression.evaluate(evalContext)); assertEquals("contains(arrayStrWithNull, null)", print(expression)); ContainsExpression primitiveNullExpression = new ContainsExpression(DSL.ref("arrayInt"), null); EvaluationException exception = - assertThrows(EvaluationException.class, () -> primitiveNullExpression.evaluate(resolver)); + assertThrows( + EvaluationException.class, () -> primitiveNullExpression.evaluate(evalContext)); assertEquals("Cannot compare null with primitive array", exception.getMessage()); assertEquals("contains(arrayInt, null)", print(primitiveNullExpression)); } @@ -140,22 +171,30 @@ void arrayExpression() { @Test void mapExpression() { ContainsExpression expression = new ContainsExpression(DSL.ref("mapStr"), DSL.value("bar")); - assertTrue(expression.evaluate(resolver)); + assertTrue(expression.evaluate(evalContext)); assertEquals("contains(mapStr, \"bar\")", print(expression)); expression = new ContainsExpression(DSL.ref("mapStrWithNull"), null); - assertTrue(expression.evaluate(resolver)); + assertTrue(expression.evaluate(evalContext)); assertEquals("contains(mapStrWithNull, null)", print(expression)); } @Test void setExpression() { ContainsExpression expression = new ContainsExpression(DSL.ref("setStr"), DSL.value("bar")); - assertTrue(expression.evaluate(resolver)); + assertTrue(expression.evaluate(evalContext)); assertEquals("contains(setStr, \"bar\")", print(expression)); expression = new ContainsExpression(DSL.ref("setStrWithNull"), null); - assertTrue(expression.evaluate(resolver)); + assertTrue(expression.evaluate(evalContext)); assertEquals("contains(setStrWithNull, null)", print(expression)); } + + static class SlowObject { + @Override + public boolean equals(Object obj) { + LockSupport.parkNanos(1000); + return super.equals(obj); + } + } } diff --git a/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/expressions/EndsWithExpressionTest.java b/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/expressions/EndsWithExpressionTest.java index 17a6b1c07f1..1ac730564a7 100644 --- a/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/expressions/EndsWithExpressionTest.java +++ b/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/expressions/EndsWithExpressionTest.java @@ -1,5 +1,6 @@ package com.datadog.debugger.el.expressions; +import static com.datadog.debugger.el.EvalContextHelper.createEvalContext; import static com.datadog.debugger.el.PrettyPrintVisitor.print; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -7,16 +8,15 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import com.datadog.debugger.el.DSL; +import com.datadog.debugger.el.EvalContext; import com.datadog.debugger.el.EvaluationException; -import com.datadog.debugger.el.RefResolverHelper; import com.datadog.debugger.el.values.StringValue; -import datadog.trace.bootstrap.debugger.el.ValueReferenceResolver; import datadog.trace.bootstrap.debugger.el.Values; import java.net.URI; import org.junit.jupiter.api.Test; class EndsWithExpressionTest { - private final ValueReferenceResolver resolver = RefResolverHelper.createResolver(this); + private final EvalContext evalContext = createEvalContext(this); // used to ref lookup URI uri = URI.create("https://www.datadoghq.com"); @@ -24,7 +24,7 @@ class EndsWithExpressionTest { void nullExpression() { EndsWithExpression expression = new EndsWithExpression(null, null); EvaluationException exception = - assertThrows(EvaluationException.class, () -> expression.evaluate(resolver)); + assertThrows(EvaluationException.class, () -> expression.evaluate(evalContext)); assertEquals("Cannot evaluate the expression for null value", exception.getMessage()); assertEquals("endsWith(null, null)", print(expression)); } @@ -34,7 +34,7 @@ void undefinedExpression() { EndsWithExpression expression = new EndsWithExpression(DSL.value(Values.UNDEFINED_OBJECT), new StringValue(null)); EvaluationException exception = - assertThrows(EvaluationException.class, () -> expression.evaluate(resolver)); + assertThrows(EvaluationException.class, () -> expression.evaluate(evalContext)); assertEquals("Cannot evaluate the expression for undefined value", exception.getMessage()); assertEquals("endsWith(UNDEFINED, \"null\")", print(expression)); } @@ -42,18 +42,18 @@ void undefinedExpression() { @Test void stringExpression() { EndsWithExpression expression = new EndsWithExpression(DSL.value("abc"), new StringValue("bc")); - assertTrue(expression.evaluate(resolver)); + assertTrue(expression.evaluate(evalContext)); assertEquals("endsWith(\"abc\", \"bc\")", print(expression)); expression = new EndsWithExpression(DSL.value("abc"), new StringValue("ab")); - assertFalse(expression.evaluate(resolver)); + assertFalse(expression.evaluate(evalContext)); assertEquals("endsWith(\"abc\", \"ab\")", print(expression)); } @Test void stringPrimitives() { EndsWithExpression expression = new EndsWithExpression(DSL.ref("uri"), new StringValue(".com")); - assertTrue(expression.evaluate(resolver)); + assertTrue(expression.evaluate(evalContext)); assertEquals("endsWith(uri, \".com\")", print(expression)); } } diff --git a/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/expressions/FilterCollectionExpressionTest.java b/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/expressions/FilterCollectionExpressionTest.java index 38b0ea7e692..6d2e80cebda 100644 --- a/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/expressions/FilterCollectionExpressionTest.java +++ b/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/expressions/FilterCollectionExpressionTest.java @@ -1,32 +1,40 @@ package com.datadog.debugger.el.expressions; import static com.datadog.debugger.el.DSL.*; +import static com.datadog.debugger.el.EvalContextHelper.createEvalContext; import static com.datadog.debugger.el.PrettyPrintVisitor.print; import static org.junit.jupiter.api.Assertions.*; +import com.datadog.debugger.el.EvalContext; import com.datadog.debugger.el.EvaluationException; -import com.datadog.debugger.el.RefResolverHelper; import com.datadog.debugger.el.values.CollectionValue; import com.datadog.debugger.el.values.ListValue; import com.datadog.debugger.el.values.MapValue; import com.datadog.debugger.el.values.SetValue; import datadog.trace.bootstrap.debugger.el.ValueReferences; import datadog.trace.bootstrap.debugger.el.Values; +import java.time.Duration; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Map; +import java.util.Set; import org.junit.jupiter.api.Test; class FilterCollectionExpressionTest { + + private final EvalContext evalContext = createEvalContext(this); + @Test void testMatchingList() { ListValue collection = new ListValue(new int[] {1, 2, 3}); FilterCollectionExpression expression = new FilterCollectionExpression(collection, lt(ref(ValueReferences.ITERATOR_REF), value(2))); - CollectionValue filtered = expression.evaluate(RefResolverHelper.createResolver(this)); + CollectionValue filtered = expression.evaluate(evalContext); assertNotEquals(collection, filtered); assertEquals(1, filtered.count()); assertFalse(filtered.isEmpty()); @@ -40,7 +48,7 @@ void testEmptyList() { ListValue collection = new ListValue(new int[0]); FilterCollectionExpression expression = new FilterCollectionExpression(collection, lt(ref(ValueReferences.ITERATOR_REF), value(2))); - CollectionValue filtered = expression.evaluate(RefResolverHelper.createResolver(this)); + CollectionValue filtered = expression.evaluate(evalContext); assertNotEquals(collection, filtered); assertTrue(filtered.isEmpty()); assertFalse(filtered.isNull()); @@ -54,9 +62,7 @@ void testNullList() { FilterCollectionExpression expression = new FilterCollectionExpression(collection, lt(ref(ValueReferences.ITERATOR_REF), value(2))); EvaluationException exception = - assertThrows( - EvaluationException.class, - () -> expression.evaluate(RefResolverHelper.createResolver(this))); + assertThrows(EvaluationException.class, () -> expression.evaluate(evalContext)); assertEquals("Cannot evaluate the expression for null value", exception.getMessage()); assertEquals("filter(null, {@it < 2})", print(expression)); } @@ -67,9 +73,7 @@ void testNullObjectList() { FilterCollectionExpression expression = new FilterCollectionExpression(collection, lt(ref(ValueReferences.ITERATOR_REF), value(2))); EvaluationException exception = - assertThrows( - EvaluationException.class, - () -> expression.evaluate(RefResolverHelper.createResolver(this))); + assertThrows(EvaluationException.class, () -> expression.evaluate(evalContext)); assertEquals("Cannot evaluate the expression for null value", exception.getMessage()); assertEquals("filter(null, {@it < 2})", print(expression)); } @@ -80,13 +84,27 @@ void testUndefinedList() { FilterCollectionExpression expression = new FilterCollectionExpression(collection, lt(ref(ValueReferences.ITERATOR_REF), value(2))); EvaluationException exception = - assertThrows( - EvaluationException.class, - () -> expression.evaluate(RefResolverHelper.createResolver(this))); + assertThrows(EvaluationException.class, () -> expression.evaluate(evalContext)); assertEquals("Cannot evaluate the expression for undefined value", exception.getMessage()); assertEquals("filter(null, {@it < 2})", print(expression)); } + @Test + void testLargeList() { + List largeList = new ArrayList<>(); + for (int i = 0; i < 1_000_000; i++) { + largeList.add(i); + } + ListValue collection = new ListValue(largeList); + FilterCollectionExpression expression = + new FilterCollectionExpression(collection, lt(ref(ValueReferences.ITERATOR_REF), value(0))); + EvalContext timeoutEvalContext = createEvalContext(this, Duration.ofMillis(1)); + EvaluationException exception = + assertThrows(EvaluationException.class, () -> expression.evaluate(timeoutEvalContext)); + assertEquals("timeout (1ms)", exception.getMessage()); + assertEquals("filter(List, {@it < 0})", print(expression)); + } + @Test void testMatchingMap() { Map map = new HashMap<>(); @@ -98,7 +116,7 @@ void testMatchingMap() { FilterCollectionExpression expression = new FilterCollectionExpression( collection, eq(getMember(ref(ValueReferences.ITERATOR_REF), "key"), value("b"))); - CollectionValue filtered = expression.evaluate(RefResolverHelper.createResolver(this)); + CollectionValue filtered = expression.evaluate(evalContext); assertNotEquals(collection, filtered); assertEquals(1, filtered.count()); assertFalse(filtered.isEmpty()); @@ -108,7 +126,7 @@ void testMatchingMap() { expression = new FilterCollectionExpression( collection, lt(getMember(ref(ValueReferences.ITERATOR_REF), "value"), value(2))); - filtered = expression.evaluate(RefResolverHelper.createResolver(this)); + filtered = expression.evaluate(evalContext); assertNotEquals(collection, filtered); assertEquals(1, filtered.count()); assertFalse(filtered.isEmpty()); @@ -122,7 +140,7 @@ void testEmptyMap() { MapValue collection = new MapValue(Collections.emptyMap()); FilterCollectionExpression expression = new FilterCollectionExpression(collection, lt(ref(ValueReferences.ITERATOR_REF), value(2))); - CollectionValue filtered = expression.evaluate(RefResolverHelper.createResolver(this)); + CollectionValue filtered = expression.evaluate(evalContext); assertNotEquals(collection, filtered); assertTrue(filtered.isEmpty()); assertFalse(filtered.isNull()); @@ -136,9 +154,7 @@ void testNullMap() { FilterCollectionExpression expression = new FilterCollectionExpression(collection, lt(ref(ValueReferences.ITERATOR_REF), value(2))); EvaluationException exception = - assertThrows( - EvaluationException.class, - () -> expression.evaluate(RefResolverHelper.createResolver(this))); + assertThrows(EvaluationException.class, () -> expression.evaluate(evalContext)); assertEquals("Cannot evaluate the expression for null value", exception.getMessage()); assertEquals("filter(null, {@it < 2})", print(expression)); } @@ -149,9 +165,7 @@ void testNullObjectMap() { FilterCollectionExpression expression = new FilterCollectionExpression(collection, lt(ref(ValueReferences.ITERATOR_REF), value(2))); EvaluationException exception = - assertThrows( - EvaluationException.class, - () -> expression.evaluate(RefResolverHelper.createResolver(this))); + assertThrows(EvaluationException.class, () -> expression.evaluate(evalContext)); assertEquals("Cannot evaluate the expression for null value", exception.getMessage()); assertEquals("filter(null, {@it < 2})", print(expression)); } @@ -162,9 +176,7 @@ void testUndefinedMap() { FilterCollectionExpression expression = new FilterCollectionExpression(collection, lt(ref(ValueReferences.ITERATOR_REF), value(2))); EvaluationException exception = - assertThrows( - EvaluationException.class, - () -> expression.evaluate(RefResolverHelper.createResolver(this))); + assertThrows(EvaluationException.class, () -> expression.evaluate(evalContext)); assertEquals("Cannot evaluate the expression for undefined value", exception.getMessage()); assertEquals("filter(null, {@it < 2})", print(expression)); } @@ -179,25 +191,42 @@ void keyValueMap() { FilterCollectionExpression expression = new FilterCollectionExpression(collection, eq(ref(ValueReferences.KEY_REF), value("b"))); - CollectionValue filtered = expression.evaluate(RefResolverHelper.createResolver(this)); + CollectionValue filtered = expression.evaluate(evalContext); assertNotEquals(collection, filtered); assertEquals(1, filtered.count()); assertEquals("filter(Map, {@key == \"b\"})", print(expression)); expression = new FilterCollectionExpression(collection, eq(ref(ValueReferences.VALUE_REF), value(2))); - filtered = expression.evaluate(RefResolverHelper.createResolver(this)); + filtered = expression.evaluate(evalContext); assertNotEquals(collection, filtered); assertEquals(1, filtered.count()); assertEquals("filter(Map, {@value == 2})", print(expression)); } + @Test + void testLargeMap() { + Map map = new HashMap<>(); + for (int i = 0; i <= 1_000_000; i++) { + map.put(i, i); + } + MapValue collection = new MapValue(map); + + FilterCollectionExpression expression = + new FilterCollectionExpression(collection, lt(ref(ValueReferences.ITERATOR_REF), value(0))); + EvalContext timeoutEvalContext = createEvalContext(this, Duration.ofMillis(1)); + EvaluationException exception = + assertThrows(EvaluationException.class, () -> expression.evaluate(timeoutEvalContext)); + assertEquals("timeout (1ms)", exception.getMessage()); + assertEquals("filter(Map, {@it < 0})", print(expression)); + } + @Test void testMatchingSet() { SetValue collection = new SetValue(new HashSet<>(Arrays.asList(1, 2, 3))); FilterCollectionExpression expression = new FilterCollectionExpression(collection, lt(ref(ValueReferences.ITERATOR_REF), value(2))); - CollectionValue filtered = expression.evaluate(RefResolverHelper.createResolver(this)); + CollectionValue filtered = expression.evaluate(evalContext); assertNotEquals(collection, filtered); assertEquals(1, filtered.count()); assertFalse(filtered.isEmpty()); @@ -211,7 +240,7 @@ void testEmptySet() { SetValue collection = new SetValue(new HashSet<>()); FilterCollectionExpression expression = new FilterCollectionExpression(collection, lt(ref(ValueReferences.ITERATOR_REF), value(2))); - CollectionValue filtered = expression.evaluate(RefResolverHelper.createResolver(this)); + CollectionValue filtered = expression.evaluate(evalContext); assertNotEquals(collection, filtered); assertTrue(filtered.isEmpty()); assertFalse(filtered.isNull()); @@ -225,9 +254,7 @@ void testNullSet() { FilterCollectionExpression expression = new FilterCollectionExpression(collection, lt(ref(ValueReferences.ITERATOR_REF), value(2))); EvaluationException exception = - assertThrows( - EvaluationException.class, - () -> expression.evaluate(RefResolverHelper.createResolver(this))); + assertThrows(EvaluationException.class, () -> expression.evaluate(evalContext)); assertEquals("Cannot evaluate the expression for null value", exception.getMessage()); assertEquals("filter(null, {@it < 2})", print(expression)); } @@ -238,9 +265,7 @@ void testNullObjectSet() { FilterCollectionExpression expression = new FilterCollectionExpression(collection, lt(ref(ValueReferences.ITERATOR_REF), value(2))); EvaluationException exception = - assertThrows( - EvaluationException.class, - () -> expression.evaluate(RefResolverHelper.createResolver(this))); + assertThrows(EvaluationException.class, () -> expression.evaluate(evalContext)); assertEquals("Cannot evaluate the expression for null value", exception.getMessage()); assertEquals("filter(null, {@it < 2})", print(expression)); } @@ -251,13 +276,28 @@ void testUndefinedSet() { FilterCollectionExpression expression = new FilterCollectionExpression(collection, lt(ref(ValueReferences.ITERATOR_REF), value(2))); EvaluationException exception = - assertThrows( - EvaluationException.class, - () -> expression.evaluate(RefResolverHelper.createResolver(this))); + assertThrows(EvaluationException.class, () -> expression.evaluate(evalContext)); assertEquals("Cannot evaluate the expression for undefined value", exception.getMessage()); assertEquals("filter(null, {@it < 2})", print(expression)); } + @Test + void testLargeSet() { + Set set = new HashSet<>(); + for (int i = 0; i <= 1_000_000; i++) { + set.add(i); + } + SetValue collection = new SetValue(set); + + FilterCollectionExpression expression = + new FilterCollectionExpression(collection, lt(ref(ValueReferences.ITERATOR_REF), value(0))); + EvalContext timeoutEvalContext = createEvalContext(this, Duration.ofMillis(1)); + EvaluationException exception = + assertThrows(EvaluationException.class, () -> expression.evaluate(timeoutEvalContext)); + assertEquals("timeout (1ms)", exception.getMessage()); + assertEquals("filter(Set, {@it < 0})", print(expression)); + } + @Test void testUnsupportedList() { ListValue collection = new ListValue(new CustomList()); @@ -265,9 +305,7 @@ void testUnsupportedList() { new FilterCollectionExpression( collection, eq(ref(ValueReferences.ITERATOR_REF), value("foo"))); EvaluationException exception = - assertThrows( - EvaluationException.class, - () -> expression.evaluate(RefResolverHelper.createResolver(this))); + assertThrows(EvaluationException.class, () -> expression.evaluate(evalContext)); assertEquals( "Unsupported List class: com.datadog.debugger.el.expressions.FilterCollectionExpressionTest$CustomList", exception.getMessage()); @@ -280,9 +318,7 @@ void testUnsupportedMap() { FilterCollectionExpression expression = new FilterCollectionExpression(collection, eq(ref(ValueReferences.VALUE_REF), value(2))); EvaluationException exception = - assertThrows( - EvaluationException.class, - () -> expression.evaluate(RefResolverHelper.createResolver(this))); + assertThrows(EvaluationException.class, () -> expression.evaluate(evalContext)); assertEquals( "Unsupported Map class: com.datadog.debugger.el.expressions.FilterCollectionExpressionTest$CustomMap", exception.getMessage()); @@ -296,9 +332,7 @@ void testUnsupportedSet() { new FilterCollectionExpression( collection, eq(ref(ValueReferences.ITERATOR_REF), value("foo"))); EvaluationException exception = - assertThrows( - EvaluationException.class, - () -> expression.evaluate(RefResolverHelper.createResolver(this))); + assertThrows(EvaluationException.class, () -> expression.evaluate(evalContext)); assertEquals( "Unsupported Set class: com.datadog.debugger.el.expressions.FilterCollectionExpressionTest$CustomSet", exception.getMessage()); diff --git a/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/expressions/GetMemberExpressionTest.java b/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/expressions/GetMemberExpressionTest.java index 088c6b7bae4..6d2263b67da 100644 --- a/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/expressions/GetMemberExpressionTest.java +++ b/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/expressions/GetMemberExpressionTest.java @@ -1,5 +1,6 @@ package com.datadog.debugger.el.expressions; +import static com.datadog.debugger.el.EvalContextHelper.createEvalContext; import static com.datadog.debugger.el.PrettyPrintVisitor.print; import static com.datadog.debugger.el.TestHelper.setFieldInConfig; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -8,7 +9,6 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import com.datadog.debugger.el.RedactedException; -import com.datadog.debugger.el.RefResolverHelper; import com.datadog.debugger.el.Value; import datadog.trace.api.Config; import datadog.trace.bootstrap.debugger.util.Redaction; @@ -16,13 +16,12 @@ import org.junit.jupiter.api.Test; class GetMemberExpressionTest { - @Test void getMemberLevel1() { GetMemberExpression expr = new GetMemberExpression(new ValueRefExpression("ref"), "b"); ObjectWithRefAndValue parent = new ObjectWithRefAndValue(null, "hello"); ExObjectWithRefAndValue instance = new ExObjectWithRefAndValue(parent, "world"); - Value val = expr.evaluate(RefResolverHelper.createResolver(instance)); + Value val = expr.evaluate(createEvalContext(instance)); assertNotNull(val); assertFalse(val.isUndefined()); assertEquals(parent.getB(), val.getValue()); @@ -37,7 +36,7 @@ void getMemberLevel2() { ObjectWithRefAndValue root = new ObjectWithRefAndValue(null, "hello"); ObjectWithRefAndValue parent = new ObjectWithRefAndValue(root, ""); ExObjectWithRefAndValue instance = new ExObjectWithRefAndValue(parent, "world"); - Value val = expr.evaluate(RefResolverHelper.createResolver(instance)); + Value val = expr.evaluate(createEvalContext(instance)); assertNotNull(val); assertFalse(val.isUndefined()); assertEquals(root.getB(), val.getValue()); @@ -47,7 +46,7 @@ void getMemberLevel2() { @Test void getMemberUndefined() { GetMemberExpression expr = new GetMemberExpression(ValueExpression.UNDEFINED, "size"); - assertEquals(Value.undefined(), expr.evaluate(RefResolverHelper.createResolver(this))); + assertEquals(Value.undefined(), expr.evaluate(createEvalContext(this))); } class StoreSecret { @@ -73,9 +72,7 @@ void redacted() { GetMemberExpression expr = new GetMemberExpression(new ValueRefExpression("store"), "password"); Holder instance = new Holder(new StoreSecret("secret123")); RedactedException redactedException = - assertThrows( - RedactedException.class, - () -> expr.evaluate(RefResolverHelper.createResolver(instance))); + assertThrows(RedactedException.class, () -> expr.evaluate(createEvalContext(instance))); assertEquals( "Could not evaluate the expression because 'store.password' was redacted", redactedException.getMessage()); @@ -93,9 +90,7 @@ void redactedType() { GetMemberExpression expr = new GetMemberExpression(new ValueRefExpression("store"), "str"); Holder instance = new Holder(new StoreSecret("secret123")); RedactedException redactedException = - assertThrows( - RedactedException.class, - () -> expr.evaluate(RefResolverHelper.createResolver(instance))); + assertThrows(RedactedException.class, () -> expr.evaluate(createEvalContext(instance))); assertEquals( "Could not evaluate the expression because 'store' was redacted", redactedException.getMessage()); @@ -115,13 +110,13 @@ class Holder { } GetMemberExpression expr = new GetMemberExpression(new ValueRefExpression("strPrimitive"), "uuid"); - Value val = expr.evaluate(RefResolverHelper.createResolver(new Holder())); + Value val = expr.evaluate(createEvalContext(new Holder())); assertNotNull(val); assertFalse(val.isUndefined()); assertEquals("123e4567-e89b-12d3-a456-426655440000", val.getValue()); assertEquals("strPrimitive.uuid", print(expr)); expr = new GetMemberExpression(new ValueRefExpression("strPrimitive"), "clazz"); - val = expr.evaluate(RefResolverHelper.createResolver(new Holder())); + val = expr.evaluate(createEvalContext(new Holder())); assertNotNull(val); assertFalse(val.isUndefined()); assertEquals("java.lang.String", val.getValue()); diff --git a/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/expressions/HasAllExpressionTest.java b/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/expressions/HasAllExpressionTest.java index 3b4f69ef0b8..0566d3537aa 100644 --- a/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/expressions/HasAllExpressionTest.java +++ b/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/expressions/HasAllExpressionTest.java @@ -1,24 +1,27 @@ package com.datadog.debugger.el.expressions; import static com.datadog.debugger.el.DSL.*; +import static com.datadog.debugger.el.EvalContextHelper.createEvalContext; import static com.datadog.debugger.el.PrettyPrintVisitor.print; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; +import com.datadog.debugger.el.EvalContext; import com.datadog.debugger.el.EvaluationException; -import com.datadog.debugger.el.RefResolverHelper; import com.datadog.debugger.el.values.ListValue; import com.datadog.debugger.el.values.MapValue; import com.datadog.debugger.el.values.SetValue; -import datadog.trace.bootstrap.debugger.el.ValueReferenceResolver; import datadog.trace.bootstrap.debugger.el.ValueReferences; import datadog.trace.bootstrap.debugger.el.Values; +import java.time.Duration; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Set; import org.junit.jupiter.api.Test; @@ -26,83 +29,83 @@ class HasAllExpressionTest { private final int testField = 10; + EvalContext evalContext = createEvalContext(this); + @Test void testNullPredicate() { - ValueReferenceResolver resolver = RefResolverHelper.createResolver(this); HasAllExpression nullExpression = new HasAllExpression(null, null); EvaluationException exception = - assertThrows(EvaluationException.class, () -> nullExpression.evaluate(resolver)); + assertThrows(EvaluationException.class, () -> nullExpression.evaluate(evalContext)); assertEquals("Cannot evaluate the expression for null value", exception.getMessage()); assertEquals("all(null, {true})", print(nullExpression)); HasAllExpression undefinedExpression = new HasAllExpression(value(Values.UNDEFINED_OBJECT), null); exception = - assertThrows(EvaluationException.class, () -> undefinedExpression.evaluate(resolver)); + assertThrows(EvaluationException.class, () -> undefinedExpression.evaluate(evalContext)); assertEquals("Cannot evaluate the expression for undefined value", exception.getMessage()); assertEquals("all(UNDEFINED, {true})", print(undefinedExpression)); HasAllExpression expression = new HasAllExpression(value(new Object[] {this}), null); - assertTrue(expression.evaluate(resolver)); + assertTrue(expression.evaluate(evalContext)); assertEquals("all(java.lang.Object[], {true})", print(expression)); expression = new HasAllExpression(value(Collections.singletonList(this)), null); - assertTrue(expression.evaluate(resolver)); + assertTrue(expression.evaluate(evalContext)); assertEquals("all(List, {true})", print(expression)); expression = new HasAllExpression(value(Collections.singletonMap(this, this)), null); - assertTrue(expression.evaluate(resolver)); + assertTrue(expression.evaluate(evalContext)); assertEquals("all(Map, {true})", print(expression)); } @Test void testNullHasAll() { - ValueReferenceResolver ctx = RefResolverHelper.createResolver(this); HasAllExpression nullExpression1 = all(null, TRUE); EvaluationException exception = - assertThrows(EvaluationException.class, () -> nullExpression1.evaluate(ctx)); + assertThrows(EvaluationException.class, () -> nullExpression1.evaluate(evalContext)); assertEquals("Cannot evaluate the expression for null value", exception.getMessage()); assertEquals("all(null, {true})", print(nullExpression1)); HasAllExpression nullExpression2 = all(null, FALSE); - exception = assertThrows(EvaluationException.class, () -> nullExpression2.evaluate(ctx)); + exception = + assertThrows(EvaluationException.class, () -> nullExpression2.evaluate(evalContext)); assertEquals("Cannot evaluate the expression for null value", exception.getMessage()); assertEquals("all(null, {false})", print(nullExpression2)); HasAllExpression nullExpression3 = all(null, eq(ref("testField"), value(10))); - exception = assertThrows(EvaluationException.class, () -> nullExpression3.evaluate(ctx)); + exception = + assertThrows(EvaluationException.class, () -> nullExpression3.evaluate(evalContext)); assertEquals("Cannot evaluate the expression for null value", exception.getMessage()); assertEquals("all(null, {testField == 10})", print(nullExpression3)); } @Test void testUndefinedHasAll() { - ValueReferenceResolver ctx = RefResolverHelper.createResolver(this); HasAllExpression undefinedExpression = all(value(Values.UNDEFINED_OBJECT), TRUE); EvaluationException exception = - assertThrows(EvaluationException.class, () -> undefinedExpression.evaluate(ctx)); + assertThrows(EvaluationException.class, () -> undefinedExpression.evaluate(evalContext)); assertEquals("Cannot evaluate the expression for undefined value", exception.getMessage()); assertEquals("all(UNDEFINED, {true})", print(undefinedExpression)); HasAllExpression nullExpression = all(null, FALSE); - exception = assertThrows(EvaluationException.class, () -> nullExpression.evaluate(ctx)); + exception = assertThrows(EvaluationException.class, () -> nullExpression.evaluate(evalContext)); assertEquals("Cannot evaluate the expression for null value", exception.getMessage()); assertEquals("all(null, {false})", print(nullExpression)); HasAllExpression expression = all(value(Values.UNDEFINED_OBJECT), eq(ref("testField"), value(10))); - exception = assertThrows(EvaluationException.class, () -> expression.evaluate(ctx)); + exception = assertThrows(EvaluationException.class, () -> expression.evaluate(evalContext)); assertEquals("Cannot evaluate the expression for undefined value", exception.getMessage()); assertEquals("all(UNDEFINED, {testField == 10})", print(expression)); } @Test void testArrayHasAll() { - ValueReferenceResolver ctx = RefResolverHelper.createResolver(this); ValueExpression targetExpression = value(new Object[] {this, "hello"}); HasAllExpression expression = all(targetExpression, TRUE); - assertTrue(expression.evaluate(ctx)); + assertTrue(expression.evaluate(evalContext)); assertEquals("all(java.lang.Object[], {true})", print(expression)); expression = all(targetExpression, FALSE); - assertFalse(expression.evaluate(ctx)); + assertFalse(expression.evaluate(evalContext)); assertEquals("all(java.lang.Object[], {false})", print(expression)); GetMemberExpression fldRef = getMember(ref(ValueReferences.ITERATOR_REF), "testField"); @@ -111,29 +114,28 @@ void testArrayHasAll() { RuntimeException runtimeException = assertThrows( RuntimeException.class, - () -> all(targetExpression, eq(fldRef, value(10))).evaluate(ctx)); + () -> all(targetExpression, eq(fldRef, value(10))).evaluate(evalContext)); assertEquals("Cannot dereference field: testField", runtimeException.getMessage()); expression = all(targetExpression, eq(itRef, value("hello"))); - assertFalse(expression.evaluate(ctx)); + assertFalse(expression.evaluate(evalContext)); assertEquals("all(java.lang.Object[], {@it == \"hello\"})", print(expression)); expression = all(targetExpression, not(isEmpty(itRef))); - assertTrue(expression.evaluate(ctx)); + assertTrue(expression.evaluate(evalContext)); assertEquals("all(java.lang.Object[], {not(isEmpty(@it))})", print(expression)); } @Test void testListHasAll() { - ValueReferenceResolver ctx = RefResolverHelper.createResolver(this); ValueExpression targetExpression = value(Arrays.asList(this, "hello")); HasAllExpression expression = all(targetExpression, TRUE); - assertTrue(expression.evaluate(ctx)); + assertTrue(expression.evaluate(evalContext)); assertEquals("all(List, {true})", print(expression)); expression = all(targetExpression, FALSE); - assertFalse(expression.evaluate(ctx)); + assertFalse(expression.evaluate(evalContext)); assertEquals("all(List, {false})", print(expression)); ValueRefExpression fldRef = ref(ValueReferences.ITERATOR_REF + "testField"); @@ -142,21 +144,36 @@ void testListHasAll() { RuntimeException runtimeException = assertThrows( RuntimeException.class, - () -> all(targetExpression, eq(fldRef, value(10))).evaluate(ctx)); + () -> all(targetExpression, eq(fldRef, value(10))).evaluate(evalContext)); assertEquals("Cannot find synthetic var: ittestField", runtimeException.getMessage()); expression = all(targetExpression, eq(itRef, value("hello"))); - assertFalse(expression.evaluate(ctx)); + assertFalse(expression.evaluate(evalContext)); assertEquals("all(List, {@it == \"hello\"})", print(expression)); expression = all(targetExpression, not(isEmpty(itRef))); - assertTrue(expression.evaluate(ctx)); + assertTrue(expression.evaluate(evalContext)); assertEquals("all(List, {not(isEmpty(@it))})", print(expression)); } + @Test + void testLargeListHasAll() { + List largeList = new ArrayList<>(); + for (int i = 0; i < 1_000_000; i++) { + largeList.add(i); + } + ValueExpression targetExpression = value(largeList); + + HasAllExpression expression = all(targetExpression, TRUE); + EvalContext timeoutEvalContext = createEvalContext(this, Duration.ofMillis(1)); + EvaluationException evaluationException = + assertThrows(EvaluationException.class, () -> expression.evaluate(timeoutEvalContext)); + assertEquals("timeout (1ms)", evaluationException.getMessage()); + assertEquals("all(List, {true})", print(expression)); + } + @Test void testMapHasAll() { - ValueReferenceResolver ctx = RefResolverHelper.createResolver(null, null); Map valueMap = new HashMap<>(); valueMap.put("a", "a"); valueMap.put("b", "a"); @@ -164,29 +181,44 @@ void testMapHasAll() { ValueExpression targetExpression = value(valueMap); HasAllExpression expression = all(targetExpression, TRUE); - assertTrue(expression.evaluate(ctx)); + assertTrue(expression.evaluate(evalContext)); assertEquals("all(Map, {true})", print(expression)); expression = all(targetExpression, FALSE); - assertFalse(expression.evaluate(ctx)); + assertFalse(expression.evaluate(evalContext)); assertEquals("all(Map, {false})", print(expression)); expression = all(targetExpression, eq(getMember(ref(ValueReferences.ITERATOR_REF), "key"), value("a"))); - assertFalse(expression.evaluate(ctx)); + assertFalse(expression.evaluate(evalContext)); assertEquals("all(Map, {@it.key == \"a\"})", print(expression)); expression = all( targetExpression, eq(getMember(ref(ValueReferences.ITERATOR_REF), "value"), value("a"))); - assertTrue(expression.evaluate(ctx)); + assertTrue(expression.evaluate(evalContext)); assertEquals("all(Map, {@it.value == \"a\"})", print(expression)); } + @Test + void testLargeMapHasAll() { + Map largeMap = new HashMap<>(); + for (int i = 0; i < 1_000_000; i++) { + largeMap.put(i, i); + } + ValueExpression targetExpression = value(largeMap); + + HasAllExpression expression = all(targetExpression, TRUE); + EvalContext timeoutEvalContext = createEvalContext(this, Duration.ofMillis(1)); + EvaluationException evaluationException = + assertThrows(EvaluationException.class, () -> expression.evaluate(timeoutEvalContext)); + assertEquals("timeout (1ms)", evaluationException.getMessage()); + assertEquals("all(Map, {true})", print(expression)); + } + @Test void testSetHasAll() { - ValueReferenceResolver ctx = RefResolverHelper.createResolver(null, null); Set valueSet = new HashSet<>(); valueSet.add("foo"); valueSet.add("bar"); @@ -194,15 +226,15 @@ void testSetHasAll() { ValueExpression targetExpression = value(valueSet); HasAllExpression expression = all(targetExpression, TRUE); - assertTrue(expression.evaluate(ctx)); + assertTrue(expression.evaluate(evalContext)); assertEquals("all(Set, {true})", print(expression)); expression = all(targetExpression, FALSE); - assertFalse(expression.evaluate(ctx)); + assertFalse(expression.evaluate(evalContext)); assertEquals("all(Set, {false})", print(expression)); expression = all(targetExpression, eq(ref(ValueReferences.ITERATOR_REF), value("key"))); - assertFalse(expression.evaluate(ctx)); + assertFalse(expression.evaluate(evalContext)); assertEquals("all(Set, {@it == \"key\"})", print(expression)); expression = @@ -211,48 +243,61 @@ void testSetHasAll() { or( eq(ref(ValueReferences.ITERATOR_REF), value("foo")), eq(ref(ValueReferences.ITERATOR_REF), value("bar")))); - assertTrue(expression.evaluate(ctx)); + assertTrue(expression.evaluate(evalContext)); assertEquals("all(Set, {@it == \"foo\" || @it == \"bar\"})", print(expression)); } + @Test + void testLargeSetHasAll() { + Set largeSet = new HashSet<>(); + for (int i = 0; i < 1_000_000; i++) { + largeSet.add(i); + } + ValueExpression targetExpression = value(largeSet); + + HasAllExpression expression = all(targetExpression, TRUE); + EvalContext timeoutEvalContext = createEvalContext(this, Duration.ofMillis(1)); + EvaluationException evaluationException = + assertThrows(EvaluationException.class, () -> expression.evaluate(timeoutEvalContext)); + assertEquals("timeout (1ms)", evaluationException.getMessage()); + assertEquals("all(Set, {true})", print(expression)); + } + @Test void emptiness() { - ValueReferenceResolver ctx = RefResolverHelper.createResolver(null, null); HasAllExpression expression = all(value(new String[] {}), TRUE); - assertTrue(expression.evaluate(ctx)); + assertTrue(expression.evaluate(evalContext)); expression = all(value(Collections.emptyList()), TRUE); - assertTrue(expression.evaluate(ctx)); + assertTrue(expression.evaluate(evalContext)); expression = all(value(Collections.emptyMap()), TRUE); - assertTrue(expression.evaluate(ctx)); + assertTrue(expression.evaluate(evalContext)); expression = all(value(Collections.emptySet()), TRUE); - assertTrue(expression.evaluate(ctx)); + assertTrue(expression.evaluate(evalContext)); } @Test void keyValueMap() { - ValueReferenceResolver ctx = RefResolverHelper.createResolver(null, null); Map valueMap = new HashMap<>(); valueMap.put("a", "a"); valueMap.put("b", "a"); ValueExpression targetExpression = value(valueMap); HasAllExpression expression = all(targetExpression, eq(ref(ValueReferences.KEY_REF), value("a"))); - assertFalse(expression.evaluate(ctx)); + assertFalse(expression.evaluate(evalContext)); assertEquals("all(Map, {@key == \"a\"})", print(expression)); expression = all(targetExpression, eq(ref(ValueReferences.VALUE_REF), value("a"))); - assertTrue(expression.evaluate(ctx)); + assertTrue(expression.evaluate(evalContext)); assertEquals("all(Map, {@value == \"a\"})", print(expression)); } @Test void testUnsupportedList() { - ValueReferenceResolver ctx = RefResolverHelper.createResolver(null, null); ListValue collection = new ListValue(new CustomList()); HasAllExpression expression = all(collection, eq(ref(ValueReferences.ITERATOR_REF), value("foo"))); EvaluationException exception = - assertThrows(EvaluationException.class, () -> expression.evaluate(ctx)); + assertThrows(EvaluationException.class, () -> expression.evaluate(evalContext)); assertEquals( "Unsupported List class: com.datadog.debugger.el.expressions.HasAllExpressionTest$CustomList", exception.getMessage()); @@ -261,11 +306,10 @@ void testUnsupportedList() { @Test void testUnsupportedMap() { - ValueReferenceResolver ctx = RefResolverHelper.createResolver(null, null); MapValue collection = new MapValue(new CustomMap()); HasAllExpression expression = all(collection, eq(ref(ValueReferences.VALUE_REF), value("foo"))); EvaluationException exception = - assertThrows(EvaluationException.class, () -> expression.evaluate(ctx)); + assertThrows(EvaluationException.class, () -> expression.evaluate(evalContext)); assertEquals( "Unsupported Map class: com.datadog.debugger.el.expressions.HasAllExpressionTest$CustomMap", exception.getMessage()); @@ -274,12 +318,11 @@ void testUnsupportedMap() { @Test void testUnsupportedSet() { - ValueReferenceResolver ctx = RefResolverHelper.createResolver(null, null); SetValue collection = new SetValue(new CustomSet()); HasAllExpression expression = all(collection, eq(ref(ValueReferences.ITERATOR_REF), value("foo"))); EvaluationException exception = - assertThrows(EvaluationException.class, () -> expression.evaluate(ctx)); + assertThrows(EvaluationException.class, () -> expression.evaluate(evalContext)); assertEquals( "Unsupported Set class: com.datadog.debugger.el.expressions.HasAllExpressionTest$CustomSet", exception.getMessage()); diff --git a/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/expressions/HasAnyExpressionTest.java b/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/expressions/HasAnyExpressionTest.java index 8a5ca4fa9cf..8d706b7b326 100644 --- a/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/expressions/HasAnyExpressionTest.java +++ b/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/expressions/HasAnyExpressionTest.java @@ -1,255 +1,300 @@ package com.datadog.debugger.el.expressions; import static com.datadog.debugger.el.DSL.*; +import static com.datadog.debugger.el.DSL.value; +import static com.datadog.debugger.el.EvalContextHelper.createEvalContext; import static com.datadog.debugger.el.PrettyPrintVisitor.print; import static org.junit.jupiter.api.Assertions.*; -import com.datadog.debugger.el.DSL; +import com.datadog.debugger.el.EvalContext; import com.datadog.debugger.el.EvaluationException; -import com.datadog.debugger.el.RefResolverHelper; import com.datadog.debugger.el.values.ListValue; import com.datadog.debugger.el.values.MapValue; import com.datadog.debugger.el.values.SetValue; -import datadog.trace.bootstrap.debugger.el.ValueReferenceResolver; import datadog.trace.bootstrap.debugger.el.ValueReferences; import datadog.trace.bootstrap.debugger.el.Values; +import java.time.Duration; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Set; import org.junit.jupiter.api.Test; class HasAnyExpressionTest { private final int testField = 10; + EvalContext evalContext = createEvalContext(this); @Test void testNullPredicate() { - ValueReferenceResolver resolver = RefResolverHelper.createResolver(this); HasAnyExpression nullExpression = new HasAnyExpression(null, null); EvaluationException exception = - assertThrows(EvaluationException.class, () -> nullExpression.evaluate(resolver)); + assertThrows(EvaluationException.class, () -> nullExpression.evaluate(evalContext)); assertEquals("Cannot evaluate the expression for null value", exception.getMessage()); assertEquals("any(null, {true})", print(nullExpression)); HasAnyExpression undefinedExpression = new HasAnyExpression(value(Values.UNDEFINED_OBJECT), null); exception = - assertThrows(EvaluationException.class, () -> undefinedExpression.evaluate(resolver)); + assertThrows(EvaluationException.class, () -> undefinedExpression.evaluate(evalContext)); assertEquals("Cannot evaluate the expression for undefined value", exception.getMessage()); assertEquals("any(UNDEFINED, {true})", print(undefinedExpression)); HasAnyExpression expression = new HasAnyExpression(value(new Object[] {this}), null); - assertTrue(expression.evaluate(resolver)); + assertTrue(expression.evaluate(evalContext)); assertEquals("any(java.lang.Object[], {true})", print(expression)); expression = new HasAnyExpression(value(Collections.singletonList(this)), null); - assertTrue(expression.evaluate(resolver)); + assertTrue(expression.evaluate(evalContext)); assertEquals("any(List, {true})", print(expression)); expression = new HasAnyExpression(value(Collections.singletonMap(this, this)), null); - assertTrue(expression.evaluate(resolver)); + assertTrue(expression.evaluate(evalContext)); assertEquals("any(Map, {true})", print(expression)); } @Test void testNullHasAny() { - ValueReferenceResolver ctx = RefResolverHelper.createResolver(this); HasAnyExpression nullExpression1 = any(null, BooleanExpression.TRUE); EvaluationException exception = - assertThrows(EvaluationException.class, () -> nullExpression1.evaluate(ctx)); + assertThrows(EvaluationException.class, () -> nullExpression1.evaluate(evalContext)); assertEquals("Cannot evaluate the expression for null value", exception.getMessage()); assertEquals("any(null, {true})", print(nullExpression1)); HasAnyExpression nullExpression2 = any(null, BooleanExpression.FALSE); - exception = assertThrows(EvaluationException.class, () -> nullExpression2.evaluate(ctx)); + exception = + assertThrows(EvaluationException.class, () -> nullExpression2.evaluate(evalContext)); assertEquals("Cannot evaluate the expression for null value", exception.getMessage()); assertEquals("any(null, {false})", print(nullExpression2)); HasAnyExpression nullExpression3 = any(null, eq(ref("testField"), value(10))); - exception = assertThrows(EvaluationException.class, () -> nullExpression3.evaluate(ctx)); + exception = + assertThrows(EvaluationException.class, () -> nullExpression3.evaluate(evalContext)); assertEquals("Cannot evaluate the expression for null value", exception.getMessage()); assertEquals("any(null, {testField == 10})", print(nullExpression3)); } @Test void testUndefinedHasAny() { - ValueReferenceResolver ctx = RefResolverHelper.createResolver(this); HasAnyExpression undefinedExpression = any(value(Values.UNDEFINED_OBJECT), TRUE); EvaluationException exception = - assertThrows(EvaluationException.class, () -> undefinedExpression.evaluate(ctx)); + assertThrows(EvaluationException.class, () -> undefinedExpression.evaluate(evalContext)); assertEquals("Cannot evaluate the expression for undefined value", exception.getMessage()); assertEquals("any(UNDEFINED, {true})", print(undefinedExpression)); HasAnyExpression nullExpression = any(null, FALSE); - exception = assertThrows(EvaluationException.class, () -> nullExpression.evaluate(ctx)); + exception = assertThrows(EvaluationException.class, () -> nullExpression.evaluate(evalContext)); assertEquals("Cannot evaluate the expression for null value", exception.getMessage()); assertEquals("any(null, {false})", print(nullExpression)); HasAnyExpression undefinedExpression2 = any(value(Values.UNDEFINED_OBJECT), eq(ref("testField"), value(10))); - exception = assertThrows(EvaluationException.class, () -> undefinedExpression2.evaluate(ctx)); + exception = + assertThrows(EvaluationException.class, () -> undefinedExpression2.evaluate(evalContext)); assertEquals("Cannot evaluate the expression for undefined value", exception.getMessage()); assertEquals("any(UNDEFINED, {testField == 10})", print(undefinedExpression2)); } @Test void testArrayHasAny() { - ValueReferenceResolver ctx = RefResolverHelper.createResolver(null, null); - ValueExpression targetExpression = DSL.value(new Object[] {this, "hello"}); + ValueExpression targetExpression = value(new Object[] {this, "hello"}); HasAnyExpression expression = any(targetExpression, TRUE); - assertTrue(expression.evaluate(ctx)); + assertTrue(expression.evaluate(evalContext)); assertEquals("any(java.lang.Object[], {true})", print(expression)); expression = any(targetExpression, FALSE); - assertFalse(expression.evaluate(ctx)); + assertFalse(expression.evaluate(evalContext)); assertEquals("any(java.lang.Object[], {false})", print(expression)); expression = any( targetExpression, eq(getMember(ref(ValueReferences.ITERATOR_REF), "testField"), value(10))); - assertTrue(expression.evaluate(ctx)); + assertTrue(expression.evaluate(evalContext)); assertEquals("any(java.lang.Object[], {@it.testField == 10})", print(expression)); expression = any(targetExpression, eq(ref(ValueReferences.ITERATOR_REF), value("hello"))); - assertTrue(expression.evaluate(ctx)); + assertTrue(expression.evaluate(evalContext)); assertEquals("any(java.lang.Object[], {@it == \"hello\"})", print(expression)); } @Test void testListHasAny() { - ValueReferenceResolver ctx = RefResolverHelper.createResolver(null, null); - ValueExpression targetExpression = DSL.value(Arrays.asList(this, "hello")); + ValueExpression targetExpression = value(Arrays.asList(this, "hello")); HasAnyExpression expression = any(targetExpression, TRUE); - assertTrue(expression.evaluate(ctx)); + assertTrue(expression.evaluate(evalContext)); assertEquals("any(List, {true})", print(expression)); expression = any(targetExpression, FALSE); - assertFalse(expression.evaluate(ctx)); + assertFalse(expression.evaluate(evalContext)); assertEquals("any(List, {false})", print(expression)); expression = any( targetExpression, eq(getMember(ref(ValueReferences.ITERATOR_REF), "testField"), value(10))); - assertTrue(expression.evaluate(ctx)); + assertTrue(expression.evaluate(evalContext)); assertEquals("any(List, {@it.testField == 10})", print(expression)); expression = any(targetExpression, eq(ref(ValueReferences.ITERATOR_REF), value("hello"))); - assertTrue(expression.evaluate(ctx)); + assertTrue(expression.evaluate(evalContext)); assertEquals("any(List, {@it == \"hello\"})", print(expression)); } + @Test + void testLargeListHasAny() { + List largeList = new ArrayList<>(); + for (int i = 0; i < 1_000_000; i++) { + largeList.add(i); + } + ValueExpression targetExpression = value(largeList); + + HasAnyExpression expression = any(targetExpression, FALSE); + EvalContext timeoutEvalContext = createEvalContext(this, Duration.ofMillis(1)); + EvaluationException evaluationException = + assertThrows(EvaluationException.class, () -> expression.evaluate(timeoutEvalContext)); + assertEquals("timeout (1ms)", evaluationException.getMessage()); + assertEquals("any(List, {false})", print(expression)); + } + @Test void testMapHasAny() { - ValueReferenceResolver ctx = RefResolverHelper.createResolver(null, null); Map valueMap = new HashMap<>(); valueMap.put("a", "a"); valueMap.put("b", null); - ValueExpression targetExpression = DSL.value(valueMap); + ValueExpression targetExpression = value(valueMap); HasAnyExpression expression = any(targetExpression, TRUE); - assertTrue(expression.evaluate(ctx)); + assertTrue(expression.evaluate(evalContext)); assertEquals("any(Map, {true})", print(expression)); expression = any(targetExpression, FALSE); - assertFalse(expression.evaluate(ctx)); + assertFalse(expression.evaluate(evalContext)); assertEquals("any(Map, {false})", print(expression)); expression = any(targetExpression, eq(getMember(ref(ValueReferences.ITERATOR_REF), "key"), value("b"))); - assertTrue(expression.evaluate(ctx)); + assertTrue(expression.evaluate(evalContext)); assertEquals("any(Map, {@it.key == \"b\"})", print(expression)); expression = any( targetExpression, eq(getMember(ref(ValueReferences.ITERATOR_REF), "value"), value("a"))); - assertTrue(expression.evaluate(ctx)); + assertTrue(expression.evaluate(evalContext)); assertEquals("any(Map, {@it.value == \"a\"})", print(expression)); expression = any(targetExpression, eq(getMember(ref(ValueReferences.ITERATOR_REF), "key"), value("c"))); - assertFalse(expression.evaluate(ctx)); + assertFalse(expression.evaluate(evalContext)); assertEquals("any(Map, {@it.key == \"c\"})", print(expression)); expression = any( targetExpression, eq(getMember(ref(ValueReferences.ITERATOR_REF), "value"), value("c"))); - assertFalse(expression.evaluate(ctx)); + assertFalse(expression.evaluate(evalContext)); assertEquals("any(Map, {@it.value == \"c\"})", print(expression)); } + @Test + void testLargeMapHasAny() { + Map largeMap = new HashMap<>(); + for (int i = 0; i < 1_000_000; i++) { + largeMap.put(i, i); + } + ValueExpression targetExpression = value(largeMap); + + HasAnyExpression expression = any(targetExpression, FALSE); + EvalContext timeoutEvalContext = createEvalContext(this, Duration.ofMillis(1)); + EvaluationException evaluationException = + assertThrows(EvaluationException.class, () -> expression.evaluate(timeoutEvalContext)); + assertEquals("timeout (1ms)", evaluationException.getMessage()); + assertEquals("any(Map, {false})", print(expression)); + } + @Test void testSetHasAny() { - ValueReferenceResolver ctx = RefResolverHelper.createResolver(null, null); Set valueSet = new HashSet<>(); valueSet.add("foo"); valueSet.add("bar"); - ValueExpression targetExpression = DSL.value(valueSet); + ValueExpression targetExpression = value(valueSet); HasAnyExpression expression = any(targetExpression, TRUE); - assertTrue(expression.evaluate(ctx)); + assertTrue(expression.evaluate(evalContext)); assertEquals("any(Set, {true})", print(expression)); - targetExpression = DSL.value(valueSet); + targetExpression = value(valueSet); expression = any(targetExpression, FALSE); - assertFalse(expression.evaluate(ctx)); + assertFalse(expression.evaluate(evalContext)); assertEquals("any(Set, {false})", print(expression)); expression = any(targetExpression, eq(ref(ValueReferences.ITERATOR_REF), value("foo"))); - assertTrue(expression.evaluate(ctx)); + assertTrue(expression.evaluate(evalContext)); assertEquals("any(Set, {@it == \"foo\"})", print(expression)); expression = any(targetExpression, eq(ref(ValueReferences.ITERATOR_REF), value("key"))); - assertFalse(expression.evaluate(ctx)); + assertFalse(expression.evaluate(evalContext)); assertEquals("any(Set, {@it == \"key\"})", print(expression)); } + @Test + void testLargeSetHasAny() { + Set largeSet = new HashSet<>(); + for (int i = 0; i < 1_000_000; i++) { + largeSet.add(i); + } + ValueExpression targetExpression = value(largeSet); + + HasAnyExpression expression = any(targetExpression, FALSE); + EvalContext timeoutEvalContext = createEvalContext(this, Duration.ofMillis(1)); + EvaluationException evaluationException = + assertThrows(EvaluationException.class, () -> expression.evaluate(timeoutEvalContext)); + assertEquals("timeout (1ms)", evaluationException.getMessage()); + assertEquals("any(Set, {false})", print(expression)); + } + @Test void emptiness() { - ValueReferenceResolver ctx = RefResolverHelper.createResolver(null, null); HasAnyExpression expression = any(value(Collections.emptyList()), TRUE); - assertFalse(expression.evaluate(ctx)); + assertFalse(expression.evaluate(evalContext)); assertEquals("any(List, {true})", print(expression)); expression = any(value(Collections.emptyMap()), TRUE); - assertFalse(expression.evaluate(ctx)); + assertFalse(expression.evaluate(evalContext)); assertEquals("any(Map, {true})", print(expression)); expression = any(value(Collections.emptySet()), TRUE); - assertFalse(expression.evaluate(ctx)); + assertFalse(expression.evaluate(evalContext)); assertEquals("any(Set, {true})", print(expression)); } @Test void keyValueMap() { - ValueReferenceResolver ctx = RefResolverHelper.createResolver(null, null); Map valueMap = new HashMap<>(); valueMap.put("a", "a"); valueMap.put("b", null); - ValueExpression targetExpression = DSL.value(valueMap); + ValueExpression targetExpression = value(valueMap); HasAnyExpression expression = any(targetExpression, eq(ref(ValueReferences.KEY_REF), value("b"))); - assertTrue(expression.evaluate(ctx)); + assertTrue(expression.evaluate(evalContext)); assertEquals("any(Map, {@key == \"b\"})", print(expression)); expression = any(targetExpression, eq(ref(ValueReferences.VALUE_REF), value("a"))); - assertTrue(expression.evaluate(ctx)); + assertTrue(expression.evaluate(evalContext)); assertEquals("any(Map, {@value == \"a\"})", print(expression)); } @Test void testUnsupportedList() { - ValueReferenceResolver ctx = RefResolverHelper.createResolver(null, null); ListValue collection = new ListValue(new CustomList()); HasAnyExpression expression = any(collection, eq(ref(ValueReferences.ITERATOR_REF), value("foo"))); EvaluationException exception = - assertThrows(EvaluationException.class, () -> expression.evaluate(ctx)); + assertThrows(EvaluationException.class, () -> expression.evaluate(evalContext)); assertEquals( "Unsupported List class: com.datadog.debugger.el.expressions.HasAnyExpressionTest$CustomList", exception.getMessage()); @@ -258,11 +303,10 @@ void testUnsupportedList() { @Test void testUnsupportedMap() { - ValueReferenceResolver ctx = RefResolverHelper.createResolver(null, null); MapValue collection = new MapValue(new CustomMap()); HasAnyExpression expression = any(collection, eq(ref(ValueReferences.VALUE_REF), value("foo"))); EvaluationException exception = - assertThrows(EvaluationException.class, () -> expression.evaluate(ctx)); + assertThrows(EvaluationException.class, () -> expression.evaluate(evalContext)); assertEquals( "Unsupported Map class: com.datadog.debugger.el.expressions.HasAnyExpressionTest$CustomMap", exception.getMessage()); @@ -271,12 +315,11 @@ void testUnsupportedMap() { @Test void testUnsupportedSet() { - ValueReferenceResolver ctx = RefResolverHelper.createResolver(null, null); SetValue collection = new SetValue(new CustomSet()); HasAnyExpression expression = any(collection, eq(ref(ValueReferences.ITERATOR_REF), value("foo"))); EvaluationException exception = - assertThrows(EvaluationException.class, () -> expression.evaluate(ctx)); + assertThrows(EvaluationException.class, () -> expression.evaluate(evalContext)); assertEquals( "Unsupported Set class: com.datadog.debugger.el.expressions.HasAnyExpressionTest$CustomSet", exception.getMessage()); diff --git a/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/expressions/IfElseExpressionTest.java b/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/expressions/IfElseExpressionTest.java index f71cd714ee5..04492d597ac 100644 --- a/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/expressions/IfElseExpressionTest.java +++ b/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/expressions/IfElseExpressionTest.java @@ -1,15 +1,17 @@ package com.datadog.debugger.el.expressions; +import static com.datadog.debugger.el.EvalContextHelper.createEvalContext; import static org.junit.jupiter.api.Assertions.*; import com.datadog.debugger.el.DSL; +import com.datadog.debugger.el.EvalContext; import com.datadog.debugger.el.Expression; -import com.datadog.debugger.el.RefResolverHelper; import com.datadog.debugger.el.values.BooleanValue; import org.junit.jupiter.api.Test; class IfElseExpressionTest { private boolean guardFlag = false; + private EvalContext evalContext = createEvalContext(this); @Test void testIfTrue() { @@ -26,7 +28,7 @@ void testIfTrue() { return null; }; IfElseExpression expression = DSL.doif(test, thenExpression, elseExpression); - expression.evaluate(RefResolverHelper.createResolver(this)); + expression.evaluate(evalContext); assertTrue(executed[0]); assertFalse(executed[1]); } @@ -45,7 +47,7 @@ void testIfFalse() { executed[1] = true; return null; }; - DSL.doif(test, thenExpression, elseExpression).evaluate(RefResolverHelper.createResolver(this)); + DSL.doif(test, thenExpression, elseExpression).evaluate(evalContext); assertFalse(executed[0]); assertTrue(executed[1]); } @@ -65,14 +67,14 @@ void testFromContext() { return null; }; guardFlag = false; - DSL.doif(test, thenExpression, elseExpression).evaluate(RefResolverHelper.createResolver(this)); + DSL.doif(test, thenExpression, elseExpression).evaluate(evalContext); assertFalse(executed[0]); assertTrue(executed[1]); executed[1] = false; guardFlag = true; - DSL.doif(test, thenExpression, elseExpression).evaluate(RefResolverHelper.createResolver(this)); + DSL.doif(test, thenExpression, elseExpression).evaluate(evalContext); assertTrue(executed[0]); assertFalse(executed[1]); } diff --git a/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/expressions/IfExpressionTest.java b/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/expressions/IfExpressionTest.java index 59685f59690..cc009684627 100644 --- a/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/expressions/IfExpressionTest.java +++ b/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/expressions/IfExpressionTest.java @@ -1,16 +1,19 @@ package com.datadog.debugger.el.expressions; +import static com.datadog.debugger.el.EvalContextHelper.createEvalContext; import static org.junit.jupiter.api.Assertions.*; import com.datadog.debugger.el.DSL; +import com.datadog.debugger.el.EvalContext; import com.datadog.debugger.el.Expression; -import com.datadog.debugger.el.RefResolverHelper; import com.datadog.debugger.el.values.BooleanValue; import org.junit.jupiter.api.Test; class IfExpressionTest { private boolean guardFlag = false; + private EvalContext evalContext = createEvalContext(this); + @Test void testIfTrue() { boolean[] executed = new boolean[] {false}; @@ -20,7 +23,7 @@ void testIfTrue() { executed[0] = true; return null; }; - DSL.doif(test, expression).evaluate(RefResolverHelper.createResolver(this)); + DSL.doif(test, expression).evaluate(evalContext); assertTrue(executed[0]); } @@ -33,7 +36,7 @@ void testIfFalse() { executed[0] = true; return null; }; - DSL.doif(test, expression).evaluate(RefResolverHelper.createResolver(this)); + DSL.doif(test, expression).evaluate(evalContext); assertFalse(executed[0]); } @@ -47,11 +50,11 @@ void testFromContext() { return null; }; guardFlag = false; - DSL.doif(test, expression).evaluate(RefResolverHelper.createResolver(this)); + DSL.doif(test, expression).evaluate(evalContext); assertFalse(executed[0]); guardFlag = true; - DSL.doif(test, expression).evaluate(RefResolverHelper.createResolver(this)); + DSL.doif(test, expression).evaluate(evalContext); assertTrue(executed[0]); } } diff --git a/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/expressions/IndexExpressionTest.java b/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/expressions/IndexExpressionTest.java index 4a6ea9ab2dd..9a36d2d054c 100644 --- a/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/expressions/IndexExpressionTest.java +++ b/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/expressions/IndexExpressionTest.java @@ -1,12 +1,13 @@ package com.datadog.debugger.el.expressions; +import static com.datadog.debugger.el.EvalContextHelper.createEvalContext; import static com.datadog.debugger.el.PrettyPrintVisitor.print; import static com.datadog.debugger.el.TestHelper.setFieldInConfig; import static org.junit.jupiter.api.Assertions.*; +import com.datadog.debugger.el.EvalContext; import com.datadog.debugger.el.EvaluationException; import com.datadog.debugger.el.RedactedException; -import com.datadog.debugger.el.RefResolverHelper; import com.datadog.debugger.el.Value; import com.datadog.debugger.el.ValueType; import com.datadog.debugger.el.values.ListValue; @@ -46,11 +47,13 @@ class IndexExpressionTest { secretMap.put("foo", new StoreSecret("secret123")); } + EvalContext evalContext = createEvalContext(this); + @Test void testArray() { IndexExpression expr = new IndexExpression(new ValueRefExpression("strArray"), new NumericValue(1, ValueType.INT)); - Value val = expr.evaluate(RefResolverHelper.createResolver(this)); + Value val = expr.evaluate(evalContext); assertNotNull(val); assertFalse(val.isUndefined()); assertEquals("bar", val.getValue()); @@ -61,7 +64,7 @@ void testArray() { void testList() { IndexExpression expr = new IndexExpression(new ValueRefExpression("strList"), new NumericValue(1, ValueType.INT)); - Value val = expr.evaluate(RefResolverHelper.createResolver(this)); + Value val = expr.evaluate(evalContext); assertNotNull(val); assertFalse(val.isUndefined()); assertEquals("bar", val.getValue()); @@ -72,7 +75,7 @@ void testList() { void testMap() { IndexExpression expr = new IndexExpression(new ValueRefExpression("strMap"), new StringValue("foo")); - Value val = expr.evaluate(RefResolverHelper.createResolver(this)); + Value val = expr.evaluate(evalContext); assertNotNull(val); assertFalse(val.isUndefined()); assertEquals("bar", val.getValue()); @@ -84,8 +87,7 @@ void testUnsupportedSet() { IndexExpression expr = new IndexExpression(new SetValue(new HashSet<>()), new StringValue("foo")); EvaluationException evaluationException = - assertThrows( - EvaluationException.class, () -> expr.evaluate(RefResolverHelper.createResolver(this))); + assertThrows(EvaluationException.class, () -> expr.evaluate(evalContext)); assertEquals( "Cannot evaluate the expression for unsupported type: com.datadog.debugger.el.values.SetValue", evaluationException.getMessage()); @@ -97,8 +99,7 @@ void testUnsupportedList() { new IndexExpression( new ListValue(new ArrayList() {}), new NumericValue(0, ValueType.INT)); EvaluationException evaluationException = - assertThrows( - EvaluationException.class, () -> expr.evaluate(RefResolverHelper.createResolver(this))); + assertThrows(EvaluationException.class, () -> expr.evaluate(evalContext)); assertEquals( "Unsupported List class: com.datadog.debugger.el.expressions.IndexExpressionTest$1", evaluationException.getMessage()); @@ -110,8 +111,7 @@ void testOutOfBoundsList() { new IndexExpression( new ListValue(new ArrayList()), new NumericValue(42, ValueType.INT)); EvaluationException evaluationException = - assertThrows( - EvaluationException.class, () -> expr.evaluate(RefResolverHelper.createResolver(this))); + assertThrows(EvaluationException.class, () -> expr.evaluate(evalContext)); assertEquals("index[42] out of bounds: [0-0]", evaluationException.getMessage()); } @@ -120,8 +120,7 @@ void testUnsupportedMap() { IndexExpression expr = new IndexExpression(new MapValue(new HashMap() {}), new StringValue("foo")); EvaluationException evaluationException = - assertThrows( - EvaluationException.class, () -> expr.evaluate(RefResolverHelper.createResolver(this))); + assertThrows(EvaluationException.class, () -> expr.evaluate(evalContext)); assertEquals( "Unsupported Map class: com.datadog.debugger.el.expressions.IndexExpressionTest$2", evaluationException.getMessage()); @@ -132,8 +131,7 @@ void undefined() { IndexExpression expr = new IndexExpression(ValueExpression.UNDEFINED, new NumericValue(1, ValueType.INT)); EvaluationException exception = - assertThrows( - EvaluationException.class, () -> expr.evaluate(RefResolverHelper.createResolver(this))); + assertThrows(EvaluationException.class, () -> expr.evaluate(evalContext)); assertEquals("Cannot evaluate the expression for undefined value", exception.getMessage()); } @@ -142,18 +140,14 @@ void redacted() { IndexExpression expr1 = new IndexExpression(new ValueRefExpression("strMap"), new StringValue("password")); RedactedException redactedException = - assertThrows( - RedactedException.class, - () -> expr1.evaluate(RefResolverHelper.createResolver(this)).getValue()); + assertThrows(RedactedException.class, () -> expr1.evaluate(evalContext).getValue()); assertEquals( "Could not evaluate the expression because 'strMap[\"password\"]' was redacted", redactedException.getMessage()); IndexExpression expr2 = new IndexExpression(new ValueRefExpression("strMap"), new ValueRefExpression("str")); redactedException = - assertThrows( - RedactedException.class, - () -> expr2.evaluate(RefResolverHelper.createResolver(this)).getValue()); + assertThrows(RedactedException.class, () -> expr2.evaluate(evalContext).getValue()); assertEquals( "Could not evaluate the expression because 'strMap[str]' was redacted", redactedException.getMessage()); @@ -172,9 +166,7 @@ void redactedType() { new IndexExpression( new ValueRefExpression("secretArray"), new NumericValue(0, ValueType.INT)); RedactedException redactedException = - assertThrows( - RedactedException.class, - () -> exprArray.evaluate(RefResolverHelper.createResolver(this)).getValue()); + assertThrows(RedactedException.class, () -> exprArray.evaluate(evalContext).getValue()); assertEquals( "Could not evaluate the expression because 'secretArray[0]' was redacted", redactedException.getMessage()); @@ -182,18 +174,14 @@ void redactedType() { new IndexExpression( new ValueRefExpression("secretList"), new NumericValue(0, ValueType.INT)); redactedException = - assertThrows( - RedactedException.class, - () -> exprList.evaluate(RefResolverHelper.createResolver(this)).getValue()); + assertThrows(RedactedException.class, () -> exprList.evaluate(evalContext).getValue()); assertEquals( "Could not evaluate the expression because 'secretList[0]' was redacted", redactedException.getMessage()); IndexExpression exprMap = new IndexExpression(new ValueRefExpression("secretMap"), new StringValue("foo")); redactedException = - assertThrows( - RedactedException.class, - () -> exprMap.evaluate(RefResolverHelper.createResolver(this)).getValue()); + assertThrows(RedactedException.class, () -> exprMap.evaluate(evalContext).getValue()); assertEquals( "Could not evaluate the expression because 'secretMap[\"foo\"]' was redacted", redactedException.getMessage()); @@ -207,12 +195,12 @@ void stringPrimitives() { IndexExpression expr = new IndexExpression( new ValueRefExpression("uuidArray"), new NumericValue(1, ValueType.INT)); - assertEquals(UUID_2, expr.evaluate(RefResolverHelper.createResolver(this)).getValue()); + assertEquals(UUID_2, expr.evaluate(evalContext).getValue()); expr = new IndexExpression(new ValueRefExpression("uuidList"), new NumericValue(1, ValueType.INT)); - assertEquals(UUID_2, expr.evaluate(RefResolverHelper.createResolver(this)).getValue()); + assertEquals(UUID_2, expr.evaluate(evalContext).getValue()); expr = new IndexExpression(new ValueRefExpression("uuidMap"), new StringValue("foo")); - assertEquals(UUID_1, expr.evaluate(RefResolverHelper.createResolver(this)).getValue()); + assertEquals(UUID_1, expr.evaluate(evalContext).getValue()); } private static class StoreSecret { diff --git a/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/expressions/IsDefinedExpressionTest.java b/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/expressions/IsDefinedExpressionTest.java index b8911c90826..53f672cd724 100644 --- a/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/expressions/IsDefinedExpressionTest.java +++ b/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/expressions/IsDefinedExpressionTest.java @@ -1,12 +1,13 @@ package com.datadog.debugger.el.expressions; +import static com.datadog.debugger.el.EvalContextHelper.createEvalContext; import static com.datadog.debugger.el.PrettyPrintVisitor.print; 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 com.datadog.debugger.el.DSL; -import com.datadog.debugger.el.RefResolverHelper; +import com.datadog.debugger.el.EvalContext; import com.datadog.debugger.el.Value; import com.datadog.debugger.el.ValueType; import com.datadog.debugger.el.values.BooleanValue; @@ -14,35 +15,34 @@ import com.datadog.debugger.el.values.MapValue; import com.datadog.debugger.el.values.NumericValue; import com.datadog.debugger.el.values.StringValue; -import datadog.trace.bootstrap.debugger.el.ValueReferenceResolver; import datadog.trace.bootstrap.debugger.el.Values; import java.util.Arrays; import java.util.Collections; import org.junit.jupiter.api.Test; class IsDefinedExpressionTest { - private final ValueReferenceResolver resolver = RefResolverHelper.createResolver(this); + private final EvalContext evalContext = createEvalContext(this); @Test void testNullValue() { IsDefinedExpression expression = new IsDefinedExpression(null); - assertFalse(expression.evaluate(resolver)); + assertFalse(expression.evaluate(evalContext)); assertEquals("isDefined(null)", print(expression)); expression = new IsDefinedExpression(DSL.value(Values.NULL_OBJECT)); - assertTrue(expression.evaluate(resolver)); + assertTrue(expression.evaluate(evalContext)); assertEquals("isDefined(null)", print(expression)); expression = new IsDefinedExpression(DSL.value(Value.nullValue())); - assertTrue(expression.evaluate(resolver)); + assertTrue(expression.evaluate(evalContext)); assertEquals("isDefined(com.datadog.debugger.el.values.NullValue)", print(expression)); } @Test void testUndefinedValue() { IsDefinedExpression expression = new IsDefinedExpression(DSL.value(Values.UNDEFINED_OBJECT)); - assertFalse(expression.evaluate(resolver)); + assertFalse(expression.evaluate(evalContext)); assertEquals("isDefined(UNDEFINED)", print(expression)); expression = new IsDefinedExpression(DSL.ref("undefinedvar")); - assertFalse(expression.evaluate(resolver)); + assertFalse(expression.evaluate(evalContext)); } @Test @@ -52,13 +52,13 @@ void testNumericLiteral() { NumericValue none = new NumericValue(null, ValueType.OBJECT); IsDefinedExpression expression = new IsDefinedExpression(zero); - assertTrue(expression.evaluate(resolver)); + assertTrue(expression.evaluate(evalContext)); assertEquals("isDefined(0)", print(expression)); expression = new IsDefinedExpression(one); - assertTrue(expression.evaluate(resolver)); + assertTrue(expression.evaluate(evalContext)); assertEquals("isDefined(1)", print(expression)); expression = new IsDefinedExpression(none); - assertTrue(expression.evaluate(resolver)); + assertTrue(expression.evaluate(evalContext)); assertEquals("isDefined(null)", print(expression)); } @@ -69,13 +69,13 @@ void testBooleanLiteral() { BooleanValue none = new BooleanValue(null, ValueType.OBJECT); IsDefinedExpression expression = new IsDefinedExpression(yes); - assertTrue(expression.evaluate(resolver)); + assertTrue(expression.evaluate(evalContext)); assertEquals("isDefined(true)", print(expression)); expression = new IsDefinedExpression(no); - assertTrue(expression.evaluate(resolver)); + assertTrue(expression.evaluate(evalContext)); assertEquals("isDefined(false)", print(expression)); expression = new IsDefinedExpression(none); - assertTrue(expression.evaluate(resolver)); + assertTrue(expression.evaluate(evalContext)); assertEquals("isDefined(null)", print(expression)); } @@ -89,11 +89,11 @@ void testStringLiteral() { IsDefinedExpression isDefined2 = new IsDefinedExpression(emptyString); IsDefinedExpression isDefined3 = new IsDefinedExpression(nullString); - assertTrue(isDefined1.evaluate(resolver)); + assertTrue(isDefined1.evaluate(evalContext)); assertEquals("isDefined(\"Hello World\")", print(isDefined1)); - assertTrue(isDefined2.evaluate(resolver)); + assertTrue(isDefined2.evaluate(evalContext)); assertEquals("isDefined(\"\")", print(isDefined2)); - assertTrue(isDefined3.evaluate(resolver)); + assertTrue(isDefined3.evaluate(evalContext)); assertEquals("isDefined(\"null\")", print(isDefined3)); } @@ -109,13 +109,13 @@ void testListValue() { IsDefinedExpression isDefined3 = new IsDefinedExpression(nullList); IsDefinedExpression isDefined4 = new IsDefinedExpression(undefinedList); - assertTrue(isDefined1.evaluate(resolver)); + assertTrue(isDefined1.evaluate(evalContext)); assertEquals("isDefined(List)", print(isDefined1)); - assertTrue(isDefined2.evaluate(resolver)); + assertTrue(isDefined2.evaluate(evalContext)); assertEquals("isDefined(List)", print(isDefined2)); - assertTrue(isDefined3.evaluate(resolver)); + assertTrue(isDefined3.evaluate(evalContext)); assertEquals("isDefined(null)", print(isDefined3)); - assertFalse(isDefined4.evaluate(resolver)); + assertFalse(isDefined4.evaluate(evalContext)); assertEquals("isDefined(null)", print(isDefined4)); } @@ -126,19 +126,18 @@ void testMapValue() { MapValue nullMao = new MapValue(null); MapValue undefinedMap = new MapValue(Values.UNDEFINED_OBJECT); - ValueReferenceResolver resolver = RefResolverHelper.createResolver(this); IsDefinedExpression isDefined1 = new IsDefinedExpression(map); IsDefinedExpression isDefined2 = new IsDefinedExpression(emptyMap); IsDefinedExpression isDefined3 = new IsDefinedExpression(nullMao); IsDefinedExpression isDefined4 = new IsDefinedExpression(undefinedMap); - assertTrue(isDefined1.evaluate(resolver)); + assertTrue(isDefined1.evaluate(evalContext)); assertEquals("isDefined(Map)", print(isDefined1)); - assertTrue(isDefined2.evaluate(resolver)); + assertTrue(isDefined2.evaluate(evalContext)); assertEquals("isDefined(Map)", print(isDefined2)); - assertTrue(isDefined3.evaluate(resolver)); + assertTrue(isDefined3.evaluate(evalContext)); assertEquals("isDefined(null)", print(isDefined3)); - assertFalse(isDefined4.evaluate(resolver)); + assertFalse(isDefined4.evaluate(evalContext)); assertEquals("isDefined(null)", print(isDefined4)); } } diff --git a/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/expressions/IsEmptyExpressionTest.java b/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/expressions/IsEmptyExpressionTest.java index d9bb0f4ecd7..3581fd96907 100644 --- a/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/expressions/IsEmptyExpressionTest.java +++ b/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/expressions/IsEmptyExpressionTest.java @@ -1,11 +1,12 @@ package com.datadog.debugger.el.expressions; +import static com.datadog.debugger.el.EvalContextHelper.createEvalContext; import static com.datadog.debugger.el.PrettyPrintVisitor.print; import static org.junit.jupiter.api.Assertions.*; import com.datadog.debugger.el.DSL; +import com.datadog.debugger.el.EvalContext; import com.datadog.debugger.el.EvaluationException; -import com.datadog.debugger.el.RefResolverHelper; import com.datadog.debugger.el.Value; import com.datadog.debugger.el.ValueType; import com.datadog.debugger.el.values.BooleanValue; @@ -13,7 +14,6 @@ import com.datadog.debugger.el.values.MapValue; import com.datadog.debugger.el.values.NumericValue; import com.datadog.debugger.el.values.StringValue; -import datadog.trace.bootstrap.debugger.el.ValueReferenceResolver; import datadog.trace.bootstrap.debugger.el.Values; import java.util.Arrays; import java.util.Collections; @@ -21,30 +21,30 @@ import org.junit.jupiter.api.Test; class IsEmptyExpressionTest { + EvalContext evalContext = createEvalContext(this); + @Test void testNullValue() { - ValueReferenceResolver resolver = RefResolverHelper.createResolver(this); IsEmptyExpression expression1 = new IsEmptyExpression(null); EvaluationException exception = - assertThrows(EvaluationException.class, () -> expression1.evaluate(resolver)); + assertThrows(EvaluationException.class, () -> expression1.evaluate(evalContext)); assertEquals("Cannot evaluate the expression for null value", exception.getMessage()); assertEquals("isEmpty(null)", print(expression1)); IsEmptyExpression expression2 = new IsEmptyExpression(DSL.value(Values.NULL_OBJECT)); - exception = assertThrows(EvaluationException.class, () -> expression2.evaluate(resolver)); + exception = assertThrows(EvaluationException.class, () -> expression2.evaluate(evalContext)); assertEquals("Cannot evaluate the expression for null value", exception.getMessage()); assertEquals("isEmpty(null)", print(expression2)); IsEmptyExpression expression3 = new IsEmptyExpression(DSL.value(Value.nullValue())); - exception = assertThrows(EvaluationException.class, () -> expression3.evaluate(resolver)); + exception = assertThrows(EvaluationException.class, () -> expression3.evaluate(evalContext)); assertEquals("Cannot evaluate the expression for null value", exception.getMessage()); assertEquals("isEmpty(com.datadog.debugger.el.values.NullValue)", print(expression3)); } @Test void testUndefinedValue() { - ValueReferenceResolver resolver = RefResolverHelper.createResolver(this); IsEmptyExpression expression = new IsEmptyExpression(DSL.value(Values.UNDEFINED_OBJECT)); EvaluationException exception = - assertThrows(EvaluationException.class, () -> expression.evaluate(resolver)); + assertThrows(EvaluationException.class, () -> expression.evaluate(evalContext)); assertEquals("Cannot evaluate the expression for undefined value", exception.getMessage()); assertEquals("isEmpty(UNDEFINED)", print(expression)); } @@ -55,15 +55,14 @@ void testNumericLiteral() { NumericValue one = new NumericValue(1, ValueType.INT); NumericValue none = new NumericValue(null, ValueType.OBJECT); - ValueReferenceResolver resolver = RefResolverHelper.createResolver(this); IsEmptyExpression expression = new IsEmptyExpression(zero); - assertFalse(expression.evaluate(resolver)); + assertFalse(expression.evaluate(evalContext)); assertEquals("isEmpty(0)", print(expression)); expression = new IsEmptyExpression(one); - assertFalse(expression.evaluate(resolver)); + assertFalse(expression.evaluate(evalContext)); assertEquals("isEmpty(1)", print(expression)); IsEmptyExpression nullExpression = new IsEmptyExpression(none); - assertThrows(EvaluationException.class, () -> nullExpression.evaluate(resolver)); + assertThrows(EvaluationException.class, () -> nullExpression.evaluate(evalContext)); assertEquals("isEmpty(null)", print(nullExpression)); } @@ -73,16 +72,15 @@ void testBooleanLiteral() { BooleanValue no = BooleanValue.FALSE; BooleanValue none = new BooleanValue(null, ValueType.OBJECT); - ValueReferenceResolver resolver = RefResolverHelper.createResolver(this); IsEmptyExpression expression = new IsEmptyExpression(yes); - assertFalse(expression.evaluate(resolver)); + assertFalse(expression.evaluate(evalContext)); assertEquals("isEmpty(true)", print(expression)); expression = new IsEmptyExpression(no); - assertFalse(expression.evaluate(resolver)); + assertFalse(expression.evaluate(evalContext)); assertEquals("isEmpty(false)", print(expression)); IsEmptyExpression nullExpression = new IsEmptyExpression(none); EvaluationException exception = - assertThrows(EvaluationException.class, () -> nullExpression.evaluate(resolver)); + assertThrows(EvaluationException.class, () -> nullExpression.evaluate(evalContext)); assertEquals("Cannot evaluate the expression for null value", exception.getMessage()); assertEquals("isEmpty(null)", print(nullExpression)); } @@ -93,17 +91,16 @@ void testStringLiteral() { StringValue emptyString = new StringValue(""); StringValue nullString = new StringValue(null); - ValueReferenceResolver resolver = RefResolverHelper.createResolver(this); IsEmptyExpression isEmpty1 = new IsEmptyExpression(string); IsEmptyExpression isEmpty2 = new IsEmptyExpression(emptyString); IsEmptyExpression isEmpty3 = new IsEmptyExpression(nullString); - assertFalse(isEmpty1.evaluate(resolver)); + assertFalse(isEmpty1.evaluate(evalContext)); assertEquals("isEmpty(\"Hello World\")", print(isEmpty1)); - assertTrue(isEmpty2.evaluate(resolver)); + assertTrue(isEmpty2.evaluate(evalContext)); assertEquals("isEmpty(\"\")", print(isEmpty2)); EvaluationException exception = - assertThrows(EvaluationException.class, () -> isEmpty3.evaluate(resolver)); + assertThrows(EvaluationException.class, () -> isEmpty3.evaluate(evalContext)); assertEquals("Cannot evaluate the expression for null value", exception.getMessage()); assertEquals("isEmpty(\"null\")", print(isEmpty3)); } @@ -117,7 +114,6 @@ void testCollectionLiteral() { ListValue set = new ListValue(new HashSet<>(Arrays.asList("a", "b"))); ListValue emptySet = new ListValue(Collections.emptySet()); - ValueReferenceResolver resolver = RefResolverHelper.createResolver(this); IsEmptyExpression isEmpty1 = new IsEmptyExpression(list); IsEmptyExpression isEmpty2 = new IsEmptyExpression(emptyList); IsEmptyExpression isEmpty3 = new IsEmptyExpression(nullList); @@ -125,20 +121,20 @@ void testCollectionLiteral() { IsEmptyExpression isEmpty5 = new IsEmptyExpression(set); IsEmptyExpression isEmpty6 = new IsEmptyExpression(emptySet); - assertFalse(isEmpty1.evaluate(resolver)); + assertFalse(isEmpty1.evaluate(evalContext)); assertEquals("isEmpty(List)", print(isEmpty1)); - assertTrue(isEmpty2.evaluate(resolver)); + assertTrue(isEmpty2.evaluate(evalContext)); assertEquals("isEmpty(List)", print(isEmpty2)); EvaluationException exception = - assertThrows(EvaluationException.class, () -> isEmpty3.evaluate(resolver)); + assertThrows(EvaluationException.class, () -> isEmpty3.evaluate(evalContext)); assertEquals("Cannot evaluate the expression for null value", exception.getMessage()); assertEquals("isEmpty(null)", print(isEmpty3)); - exception = assertThrows(EvaluationException.class, () -> isEmpty4.evaluate(resolver)); + exception = assertThrows(EvaluationException.class, () -> isEmpty4.evaluate(evalContext)); assertEquals("Cannot evaluate the expression for undefined value", exception.getMessage()); assertEquals("isEmpty(null)", print(isEmpty4)); - assertFalse(isEmpty5.evaluate(resolver)); + assertFalse(isEmpty5.evaluate(evalContext)); assertEquals("isEmpty(Set)", print(isEmpty5)); - assertTrue(isEmpty6.evaluate(resolver)); + assertTrue(isEmpty6.evaluate(evalContext)); assertEquals("isEmpty(Set)", print(isEmpty6)); } @@ -149,21 +145,20 @@ void testMapValue() { MapValue nullMao = new MapValue(null); MapValue undefinedMap = new MapValue(Values.UNDEFINED_OBJECT); - ValueReferenceResolver resolver = RefResolverHelper.createResolver(this); IsEmptyExpression isEmpty1 = new IsEmptyExpression(map); IsEmptyExpression isEmpty2 = new IsEmptyExpression(emptyMap); IsEmptyExpression isEmpty3 = new IsEmptyExpression(nullMao); IsEmptyExpression isEmpty4 = new IsEmptyExpression(undefinedMap); - assertFalse(isEmpty1.evaluate(resolver)); + assertFalse(isEmpty1.evaluate(evalContext)); assertEquals("isEmpty(Map)", print(isEmpty1)); - assertTrue(isEmpty2.evaluate(resolver)); + assertTrue(isEmpty2.evaluate(evalContext)); assertEquals("isEmpty(Map)", print(isEmpty2)); EvaluationException exception = - assertThrows(EvaluationException.class, () -> isEmpty3.evaluate(resolver)); + assertThrows(EvaluationException.class, () -> isEmpty3.evaluate(evalContext)); assertEquals("Cannot evaluate the expression for null value", exception.getMessage()); assertEquals("isEmpty(null)", print(isEmpty3)); - exception = assertThrows(EvaluationException.class, () -> isEmpty4.evaluate(resolver)); + exception = assertThrows(EvaluationException.class, () -> isEmpty4.evaluate(evalContext)); assertEquals("Cannot evaluate the expression for undefined value", exception.getMessage()); assertEquals("isEmpty(null)", print(isEmpty4)); } diff --git a/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/expressions/LenExpressionTest.java b/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/expressions/LenExpressionTest.java index a978e6a8a42..5c9d150141c 100644 --- a/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/expressions/LenExpressionTest.java +++ b/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/expressions/LenExpressionTest.java @@ -1,12 +1,12 @@ package com.datadog.debugger.el.expressions; +import static com.datadog.debugger.el.EvalContextHelper.createEvalContext; import static com.datadog.debugger.el.PrettyPrintVisitor.print; import static org.junit.jupiter.api.Assertions.*; import com.datadog.debugger.el.DSL; +import com.datadog.debugger.el.EvalContext; import com.datadog.debugger.el.EvaluationException; -import com.datadog.debugger.el.RefResolverHelper; -import datadog.trace.bootstrap.debugger.el.ValueReferenceResolver; import datadog.trace.bootstrap.debugger.el.Values; import java.util.Arrays; import java.util.Collections; @@ -14,13 +14,13 @@ import org.junit.jupiter.api.Test; class LenExpressionTest { - private final ValueReferenceResolver resolver = RefResolverHelper.createResolver(this); + private final EvalContext evalContext = createEvalContext(this); @Test void nullExpression() { LenExpression expression = new LenExpression(null); EvaluationException exception = - assertThrows(EvaluationException.class, () -> expression.evaluate(resolver).getValue()); + assertThrows(EvaluationException.class, () -> expression.evaluate(evalContext).getValue()); assertEquals("Cannot evaluate the expression for null value", exception.getMessage()); assertEquals("len(null)", print(expression)); } @@ -29,7 +29,7 @@ void nullExpression() { void undefinedExpression() { LenExpression expression = new LenExpression(DSL.value(Values.UNDEFINED_OBJECT)); EvaluationException exception = - assertThrows(EvaluationException.class, () -> expression.evaluate(resolver).getValue()); + assertThrows(EvaluationException.class, () -> expression.evaluate(evalContext).getValue()); assertEquals("Cannot evaluate the expression for undefined value", exception.getMessage()); assertEquals("len(UNDEFINED)", print(expression)); } @@ -37,24 +37,24 @@ void undefinedExpression() { @Test void stringExpression() { LenExpression expression = new LenExpression(DSL.value("a")); - assertEquals(1, expression.evaluate(resolver).getValue()); + assertEquals(1, expression.evaluate(evalContext).getValue()); assertEquals("len(\"a\")", print(expression)); } @Test void collectionExpression() { LenExpression expression = new LenExpression(DSL.value(Arrays.asList("a", "b"))); - assertEquals(2, expression.evaluate(resolver).getValue()); + assertEquals(2, expression.evaluate(evalContext).getValue()); assertEquals("len(List)", print(expression)); expression = new LenExpression(DSL.value(new HashSet<>(Arrays.asList("a", "b")))); - assertEquals(2, expression.evaluate(resolver).getValue()); + assertEquals(2, expression.evaluate(evalContext).getValue()); assertEquals("len(Set)", print(expression)); } @Test void mapExpression() { LenExpression expression = new LenExpression(DSL.value(Collections.singletonMap("a", "b"))); - assertEquals(1, expression.evaluate(resolver).getValue()); + assertEquals(1, expression.evaluate(evalContext).getValue()); assertEquals("len(Map)", print(expression)); } } diff --git a/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/expressions/MatchesExpressionTest.java b/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/expressions/MatchesExpressionTest.java index 23cfc0c7c66..7e33d745532 100644 --- a/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/expressions/MatchesExpressionTest.java +++ b/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/expressions/MatchesExpressionTest.java @@ -1,19 +1,19 @@ package com.datadog.debugger.el.expressions; +import static com.datadog.debugger.el.EvalContextHelper.createEvalContext; import static com.datadog.debugger.el.PrettyPrintVisitor.print; import static org.junit.jupiter.api.Assertions.*; import com.datadog.debugger.el.DSL; +import com.datadog.debugger.el.EvalContext; import com.datadog.debugger.el.EvaluationException; -import com.datadog.debugger.el.RefResolverHelper; import com.datadog.debugger.el.values.StringValue; -import datadog.trace.bootstrap.debugger.el.ValueReferenceResolver; import datadog.trace.bootstrap.debugger.el.Values; import java.net.URI; import org.junit.jupiter.api.Test; class MatchesExpressionTest { - private final ValueReferenceResolver resolver = RefResolverHelper.createResolver(this); + private final EvalContext evalContext = createEvalContext(this); // used to ref lookup URI uri = URI.create("https://www.datadoghq.com"); @@ -21,7 +21,7 @@ class MatchesExpressionTest { void nullExpression() { MatchesExpression expression = new MatchesExpression(null, null); EvaluationException exception = - assertThrows(EvaluationException.class, () -> expression.evaluate(resolver)); + assertThrows(EvaluationException.class, () -> expression.evaluate(evalContext)); assertEquals("Cannot evaluate the expression for null value", exception.getMessage()); assertEquals("matches(null, null)", print(expression)); } @@ -31,7 +31,7 @@ void undefinedExpression() { MatchesExpression expression = new MatchesExpression(DSL.value(Values.UNDEFINED_OBJECT), new StringValue(null)); EvaluationException exception = - assertThrows(EvaluationException.class, () -> expression.evaluate(resolver)); + assertThrows(EvaluationException.class, () -> expression.evaluate(evalContext)); assertEquals("Cannot evaluate the expression for undefined value", exception.getMessage()); assertEquals("matches(UNDEFINED, \"null\")", print(expression)); } @@ -39,17 +39,17 @@ void undefinedExpression() { @Test void stringExpression() { MatchesExpression expression = new MatchesExpression(DSL.value("abc"), new StringValue("abc")); - assertTrue(expression.evaluate(resolver)); + assertTrue(expression.evaluate(evalContext)); assertEquals("matches(\"abc\", \"abc\")", print(expression)); expression = new MatchesExpression(DSL.value("abc"), new StringValue("^ab.*")); - assertTrue(expression.evaluate(resolver)); + assertTrue(expression.evaluate(evalContext)); assertEquals("matches(\"abc\", \"^ab.*\")", print(expression)); expression = new MatchesExpression(DSL.value("abc"), new StringValue("bc")); - assertFalse(expression.evaluate(resolver)); + assertFalse(expression.evaluate(evalContext)); assertEquals("matches(\"abc\", \"bc\")", print(expression)); expression = new MatchesExpression(DSL.value("abc"), new StringValue("[def]+")); - assertFalse(expression.evaluate(resolver)); + assertFalse(expression.evaluate(evalContext)); assertEquals("matches(\"abc\", \"[def]+\")", print(expression)); } @@ -57,7 +57,7 @@ void stringExpression() { void stringPrimitives() { MatchesExpression expression = new MatchesExpression(DSL.ref("uri"), new StringValue("^https?://w{3}\\.datadoghq\\.com$")); - assertTrue(expression.evaluate(resolver)); + assertTrue(expression.evaluate(evalContext)); assertEquals("matches(uri, \"^https?://w{3}\\.datadoghq\\.com$\")", print(expression)); } } diff --git a/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/expressions/NotExpressionTest.java b/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/expressions/NotExpressionTest.java index ac6d251134c..bad4a9f4c0e 100644 --- a/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/expressions/NotExpressionTest.java +++ b/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/expressions/NotExpressionTest.java @@ -1,9 +1,9 @@ package com.datadog.debugger.el.expressions; +import static com.datadog.debugger.el.EvalContextHelper.createEvalContext; import static com.datadog.debugger.el.PrettyPrintVisitor.print; import static org.junit.jupiter.api.Assertions.*; -import com.datadog.debugger.el.RefResolverHelper; import java.util.stream.Stream; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; @@ -14,7 +14,7 @@ class NotExpressionTest { @MethodSource("expressions") void testNullPredicate(BooleanExpression expression, boolean expected, String prettyPrint) { NotExpression expr = new NotExpression(expression); - assertEquals(expected, expr.evaluate(RefResolverHelper.createResolver(this))); + assertEquals(expected, expr.evaluate(createEvalContext(this))); assertEquals(prettyPrint, print(expr)); } diff --git a/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/expressions/StartsWithExpressionTest.java b/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/expressions/StartsWithExpressionTest.java index 0626dc1d6ff..f17a11a7946 100644 --- a/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/expressions/StartsWithExpressionTest.java +++ b/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/expressions/StartsWithExpressionTest.java @@ -1,19 +1,19 @@ package com.datadog.debugger.el.expressions; +import static com.datadog.debugger.el.EvalContextHelper.createEvalContext; import static com.datadog.debugger.el.PrettyPrintVisitor.print; import static org.junit.jupiter.api.Assertions.*; import com.datadog.debugger.el.DSL; +import com.datadog.debugger.el.EvalContext; import com.datadog.debugger.el.EvaluationException; -import com.datadog.debugger.el.RefResolverHelper; import com.datadog.debugger.el.values.StringValue; -import datadog.trace.bootstrap.debugger.el.ValueReferenceResolver; import datadog.trace.bootstrap.debugger.el.Values; import java.net.URI; import org.junit.jupiter.api.Test; class StartsWithExpressionTest { - private final ValueReferenceResolver resolver = RefResolverHelper.createResolver(this); + private final EvalContext evalContext = createEvalContext(this); // used to ref lookup URI uri = URI.create("https://www.datadoghq.com"); @@ -21,7 +21,7 @@ class StartsWithExpressionTest { void nullExpression() { StartsWithExpression expression = new StartsWithExpression(null, null); EvaluationException exception = - assertThrows(EvaluationException.class, () -> expression.evaluate(resolver)); + assertThrows(EvaluationException.class, () -> expression.evaluate(evalContext)); assertEquals("Cannot evaluate the expression for null value", exception.getMessage()); assertEquals("startsWith(null, null)", print(expression)); } @@ -31,7 +31,7 @@ void undefinedExpression() { StartsWithExpression expression = new StartsWithExpression(DSL.value(Values.UNDEFINED_OBJECT), new StringValue(null)); EvaluationException exception = - assertThrows(EvaluationException.class, () -> expression.evaluate(resolver)); + assertThrows(EvaluationException.class, () -> expression.evaluate(evalContext)); assertEquals("Cannot evaluate the expression for undefined value", exception.getMessage()); assertEquals("startsWith(UNDEFINED, \"null\")", print(expression)); } @@ -40,11 +40,11 @@ void undefinedExpression() { void stringExpression() { StartsWithExpression expression = new StartsWithExpression(DSL.value("abc"), new StringValue("ab")); - assertTrue(expression.evaluate(resolver)); + assertTrue(expression.evaluate(evalContext)); assertEquals("startsWith(\"abc\", \"ab\")", print(expression)); expression = new StartsWithExpression(DSL.value("abc"), new StringValue("bc")); - assertFalse(expression.evaluate(resolver)); + assertFalse(expression.evaluate(evalContext)); assertEquals("startsWith(\"abc\", \"bc\")", print(expression)); } @@ -52,7 +52,7 @@ void stringExpression() { void stringPrimitives() { StartsWithExpression expression = new StartsWithExpression(DSL.ref("uri"), new StringValue("https")); - assertTrue(expression.evaluate(resolver)); + assertTrue(expression.evaluate(evalContext)); assertEquals("startsWith(uri, \"https\")", print(expression)); } } diff --git a/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/expressions/SubStringExpressionTest.java b/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/expressions/SubStringExpressionTest.java index 57c2ddb8291..3e2479cdd42 100644 --- a/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/expressions/SubStringExpressionTest.java +++ b/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/expressions/SubStringExpressionTest.java @@ -1,19 +1,19 @@ package com.datadog.debugger.el.expressions; +import static com.datadog.debugger.el.EvalContextHelper.createEvalContext; import static com.datadog.debugger.el.PrettyPrintVisitor.print; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import com.datadog.debugger.el.DSL; +import com.datadog.debugger.el.EvalContext; import com.datadog.debugger.el.EvaluationException; -import com.datadog.debugger.el.RefResolverHelper; -import datadog.trace.bootstrap.debugger.el.ValueReferenceResolver; import datadog.trace.bootstrap.debugger.el.Values; import java.net.URI; import org.junit.jupiter.api.Test; public class SubStringExpressionTest { - private final ValueReferenceResolver resolver = RefResolverHelper.createResolver(this); + private final EvalContext evalContext = createEvalContext(this); // used to ref lookup URI uri = URI.create("https://www.datadoghq.com"); Object nullValue = null; @@ -22,11 +22,11 @@ public class SubStringExpressionTest { void nullExpression() { SubStringExpression expression1 = new SubStringExpression(null, 0, 0); EvaluationException evaluationException = - assertThrows(EvaluationException.class, () -> expression1.evaluate(resolver)); + assertThrows(EvaluationException.class, () -> expression1.evaluate(evalContext)); assertEquals("substring(null, 0, 0)", evaluationException.getExpr()); SubStringExpression expression2 = new SubStringExpression(DSL.ref("nullValue"), 0, 0); evaluationException = - assertThrows(EvaluationException.class, () -> expression2.evaluate(resolver)); + assertThrows(EvaluationException.class, () -> expression2.evaluate(evalContext)); assertEquals("substring(nullValue, 0, 0)", evaluationException.getExpr()); } @@ -35,14 +35,14 @@ void undefinedExpression() { SubStringExpression expression = new SubStringExpression(DSL.value(Values.UNDEFINED_OBJECT), 0, 0); EvaluationException evaluationException = - assertThrows(EvaluationException.class, () -> expression.evaluate(resolver)); + assertThrows(EvaluationException.class, () -> expression.evaluate(evalContext)); assertEquals("substring(UNDEFINED, 0, 0)", evaluationException.getExpr()); } @Test void stringExpression() { SubStringExpression expression = new SubStringExpression(DSL.value("abc"), 0, 1); - assertEquals("a", expression.evaluate(resolver).getValue()); + assertEquals("a", expression.evaluate(evalContext).getValue()); assertEquals("substring(\"abc\", 0, 1)", print(expression)); } @@ -50,14 +50,14 @@ void stringExpression() { void stringOutOfBoundsExpression() { SubStringExpression expression = new SubStringExpression(DSL.value("abc"), 0, 10); EvaluationException evaluationException = - assertThrows(EvaluationException.class, () -> expression.evaluate(resolver)); + assertThrows(EvaluationException.class, () -> expression.evaluate(evalContext)); assertEquals("substring(\"abc\", 0, 10)", evaluationException.getExpr()); } @Test void stringPrimitives() { SubStringExpression expression = new SubStringExpression(DSL.ref("uri"), 0, 5); - assertEquals("https", expression.evaluate(resolver).getValue()); + assertEquals("https", expression.evaluate(evalContext).getValue()); assertEquals("substring(uri, 0, 5)", print(expression)); } } diff --git a/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/expressions/ValueRefExpressionTest.java b/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/expressions/ValueRefExpressionTest.java index c5501252962..7bc26b1540e 100644 --- a/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/expressions/ValueRefExpressionTest.java +++ b/dd-java-agent/agent-debugger/debugger-el/src/test/java/com/datadog/debugger/el/expressions/ValueRefExpressionTest.java @@ -1,19 +1,22 @@ package com.datadog.debugger.el.expressions; import static com.datadog.debugger.el.DSL.*; +import static com.datadog.debugger.el.EvalContextHelper.TEST_TIMEOUT; +import static com.datadog.debugger.el.EvalContextHelper.createEvalContext; +import static com.datadog.debugger.el.EvalContextHelper.createResolver; import static com.datadog.debugger.el.PrettyPrintVisitor.print; import static com.datadog.debugger.el.TestHelper.setFieldInConfig; import static org.junit.jupiter.api.Assertions.*; import com.datadog.debugger.el.DSL; +import com.datadog.debugger.el.EvalContext; import com.datadog.debugger.el.RedactedException; -import com.datadog.debugger.el.RefResolverHelper; import com.datadog.debugger.el.Value; import datadog.trace.api.Config; import datadog.trace.bootstrap.debugger.CapturedContext.CapturedValue; -import datadog.trace.bootstrap.debugger.el.ValueReferenceResolver; import datadog.trace.bootstrap.debugger.el.ValueReferences; import datadog.trace.bootstrap.debugger.util.Redaction; +import datadog.trace.bootstrap.debugger.util.TimeoutChecker; import java.util.HashMap; import java.util.Map; import java.util.UUID; @@ -26,7 +29,7 @@ class ValueRefExpressionTest { void testRef() { ValueRefExpression valueRef = new ValueRefExpression("b"); ExObjectWithRefAndValue instance = new ExObjectWithRefAndValue(null, "hello"); - Value val = valueRef.evaluate(RefResolverHelper.createResolver(instance)); + Value val = valueRef.evaluate(createEvalContext(instance)); assertNotNull(val); assertFalse(val.isUndefined()); assertEquals(instance.getB(), val.getValue()); @@ -42,19 +45,21 @@ void testPredicatedRef() { IsEmptyExpression isEmptyInvalid = new IsEmptyExpression(invalidValueRef); ExObjectWithRefAndValue instance = new ExObjectWithRefAndValue(null, "hello"); - ValueReferenceResolver ctx = RefResolverHelper.createResolver(instance); + EvalContext evalContext = createEvalContext(instance); - assertFalse(isEmpty.evaluate(ctx)); + assertFalse(isEmpty.evaluate(evalContext)); assertEquals("isEmpty(b)", print(isEmpty)); RuntimeException runtimeException = - assertThrows(RuntimeException.class, () -> isEmptyInvalid.evaluate(ctx)); + assertThrows(RuntimeException.class, () -> isEmptyInvalid.evaluate(evalContext)); assertEquals("Cannot dereference field: x", runtimeException.getMessage()); runtimeException = - assertThrows(RuntimeException.class, () -> and(isEmptyInvalid, isEmpty).evaluate(ctx)); + assertThrows( + RuntimeException.class, () -> and(isEmptyInvalid, isEmpty).evaluate(evalContext)); assertEquals("Cannot dereference field: x", runtimeException.getMessage()); runtimeException = - assertThrows(RuntimeException.class, () -> or(isEmptyInvalid, isEmpty).evaluate(ctx)); + assertThrows( + RuntimeException.class, () -> or(isEmptyInvalid, isEmpty).evaluate(evalContext)); assertEquals("Cannot dereference field: x", runtimeException.getMessage()); assertEquals("isEmpty(x)", print(isEmptyInvalid)); } @@ -74,30 +79,33 @@ class Obj { exts.put(ValueReferences.RETURN_EXTENSION_NAME, CapturedValue.of(returnVal)); exts.put(ValueReferences.DURATION_EXTENSION_NAME, CapturedValue.of(duration)); exts.put(ValueReferences.EXCEPTION_EXTENSION_NAME, CapturedValue.of(exception)); - ValueReferenceResolver resolver = - RefResolverHelper.createResolver(new Obj()).withExtensions(exts); + EvalContext evalContext = + new EvalContext( + createResolver(new Obj()).withExtensions(exts), + TimeoutChecker.create(Config.get(), TEST_TIMEOUT)); ValueRefExpression expression = DSL.ref(ValueReferences.DURATION_REF); - assertEquals(duration, expression.evaluate(resolver).getValue()); + assertEquals(duration, expression.evaluate(evalContext).getValue()); assertEquals("@duration", print(expression)); expression = DSL.ref(ValueReferences.RETURN_REF); - assertEquals(returnVal, expression.evaluate(resolver).getValue()); + assertEquals(returnVal, expression.evaluate(evalContext).getValue()); assertEquals("@return", print(expression)); expression = DSL.ref(ValueReferences.EXCEPTION_REF); - assertEquals(exception, expression.evaluate(resolver).getValue()); + assertEquals(exception, expression.evaluate(evalContext).getValue()); assertEquals("@exception", print(expression)); expression = DSL.ref("limit"); - assertEquals(511L, expression.evaluate(resolver).getValue()); + assertEquals(511L, expression.evaluate(evalContext).getValue()); assertEquals("limit", print(expression)); expression = DSL.ref("msg"); - assertEquals("Hello there", expression.evaluate(resolver).getValue()); + assertEquals("Hello there", expression.evaluate(evalContext).getValue()); assertEquals("msg", print(expression)); expression = DSL.ref("i"); - assertEquals(6, expression.evaluate(resolver).getValue()); // int value is widened to long + assertEquals(6, expression.evaluate(evalContext).getValue()); // int value is widened to long assertEquals("i", print(expression)); ValueRefExpression invalidExpression = ref(ValueReferences.synthetic("invalid")); RuntimeException runtimeException = - assertThrows(RuntimeException.class, () -> invalidExpression.evaluate(resolver).getValue()); + assertThrows( + RuntimeException.class, () -> invalidExpression.evaluate(evalContext).getValue()); assertEquals("Cannot find synthetic var: invalid", runtimeException.getMessage()); assertEquals("@invalid", print(invalidExpression)); } @@ -115,9 +123,7 @@ public void redacted() { ValueRefExpression valueRef = new ValueRefExpression("password"); StoreSecret instance = new StoreSecret("secret123"); RedactedException redactedException = - assertThrows( - RedactedException.class, - () -> valueRef.evaluate(RefResolverHelper.createResolver(instance))); + assertThrows(RedactedException.class, () -> valueRef.evaluate(createEvalContext(instance))); assertEquals( "Could not evaluate the expression because 'password' was redacted", redactedException.getMessage()); @@ -136,8 +142,7 @@ class Holder { } RedactedException redactedException = assertThrows( - RedactedException.class, - () -> valueRef.evaluate(RefResolverHelper.createResolver(new Holder()))); + RedactedException.class, () -> valueRef.evaluate(createEvalContext(new Holder()))); assertEquals( "Could not evaluate the expression because 'store' was redacted", redactedException.getMessage()); @@ -153,13 +158,13 @@ class StrPrimitive { Class clazz = String.class; } ValueRefExpression valueRef = new ValueRefExpression("uuid"); - Value val = valueRef.evaluate(RefResolverHelper.createResolver(new StrPrimitive())); + Value val = valueRef.evaluate(createEvalContext(new StrPrimitive())); assertNotNull(val); assertFalse(val.isUndefined()); assertEquals("123e4567-e89b-12d3-a456-426655440000", val.getValue()); assertEquals("uuid", print(valueRef)); valueRef = new ValueRefExpression("clazz"); - val = valueRef.evaluate(RefResolverHelper.createResolver(new StrPrimitive())); + val = valueRef.evaluate(createEvalContext(new StrPrimitive())); assertNotNull(val); assertFalse(val.isUndefined()); assertEquals("java.lang.String", val.getValue()); diff --git a/dd-java-agent/agent-debugger/debugger-el/src/test/resources/test_conditional_14.json b/dd-java-agent/agent-debugger/debugger-el/src/test/resources/test_conditional_14.json index 69577b9e305..787c7f74691 100644 --- a/dd-java-agent/agent-debugger/debugger-el/src/test/resources/test_conditional_14.json +++ b/dd-java-agent/agent-debugger/debugger-el/src/test/resources/test_conditional_14.json @@ -7,7 +7,8 @@ {"eq": [{"count": {"ref": "strArray"}}, 2]}, {"eq": [{"count": {"ref": "strMap"}}, 2]}, {"eq": [{"count": {"ref": "strSet"}}, 1]}, - {"eq": [{"count": {"ref": "strList"}}, 1]} + {"eq": [{"count": {"ref": "strList"}}, 1]}, + {"all": [{"ref": "largeList"}, {"neq": [{"ref": "@it"}, "d"]}]} ] } } diff --git a/dd-java-agent/agent-debugger/debugger-el/src/test/resources/test_value_expr_01.json b/dd-java-agent/agent-debugger/debugger-el/src/test/resources/test_value_expr_01.json index f19b01f7f57..4c8ec9294cb 100644 --- a/dd-java-agent/agent-debugger/debugger-el/src/test/resources/test_value_expr_01.json +++ b/dd-java-agent/agent-debugger/debugger-el/src/test/resources/test_value_expr_01.json @@ -1,9 +1,9 @@ { "dsl": "", "json": { - "and": [ + "or": [ { - "or": [ + "and": [ {"eq": [{"ref": "i"}, 10]}, {"==": [{"ref": "i"}, 10]}, {"lt": [{"ref": "i"}, 11]}, @@ -20,6 +20,7 @@ {"not": {"isEmpty": {"ref": "list"}}}, {"any": [{"ref": "list"}, {"eq": [{"ref": "@it"}, "a"]}]}, {"all": [{"ref": "list"}, {"neq": [{"ref": "@it"}, "d"]}]}, + {"all": [{"ref": "largeList"}, {"neq": [{"ref": "@it"}, "d"]}]}, {"startsWith": [{"ref": "str"}, "hel"]}, {"endsWith": [{"ref": "str"}, "llo"]}, {"contains": [{"ref": "str"}, "ll"]}, diff --git a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/JsonSnapshotSerializer.java b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/JsonSnapshotSerializer.java index f3971f0d331..4acfef797f9 100644 --- a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/JsonSnapshotSerializer.java +++ b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/JsonSnapshotSerializer.java @@ -10,14 +10,12 @@ import datadog.trace.bootstrap.debugger.CapturedContext; import datadog.trace.bootstrap.debugger.DebuggerContext; import java.time.Duration; -import java.time.temporal.ChronoUnit; /** Serializes snapshots in Json using Moshi */ public class JsonSnapshotSerializer implements DebuggerContext.ValueSerializer { private static final JsonAdapter ADAPTER = MoshiHelper.createMoshiSnapshot( - Duration.of( - Config.get().getDynamicInstrumentationCaptureTimeout(), ChronoUnit.MILLIS)) + Duration.ofMillis(Config.get().getDynamicInstrumentationCaptureTimeout())) .adapter(IntakeRequest.class); private static final JsonAdapter VALUE_ADAPTER = new MoshiSnapshotHelper.CapturedValueAdapter(); diff --git a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/StringTemplateBuilder.java b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/StringTemplateBuilder.java index 8bd75149e39..893d1a43d65 100644 --- a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/StringTemplateBuilder.java +++ b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/StringTemplateBuilder.java @@ -7,10 +7,13 @@ import com.datadog.debugger.el.Value; import com.datadog.debugger.el.ValueScript; import com.datadog.debugger.probe.LogProbe; +import datadog.trace.api.Config; import datadog.trace.bootstrap.debugger.CapturedContext; import datadog.trace.bootstrap.debugger.EvaluationError; import datadog.trace.bootstrap.debugger.Limits; import datadog.trace.bootstrap.debugger.util.Redaction; +import datadog.trace.bootstrap.debugger.util.TimeoutChecker; +import java.time.Duration; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -37,6 +40,9 @@ public String evaluate(CapturedContext context, LogProbe.LogStatus status) { return null; } StringBuilder sb = new StringBuilder(); + // Only one timeout for all expressions + Duration timeout = Duration.ofMillis(Config.get().getDynamicInstrumentationEvalTimeout()); + TimeoutChecker timeoutChecker = TimeoutChecker.create(Config.get(), timeout); for (LogProbe.Segment segment : segments) { ValueScript parsedExr = segment.getParsedExpr(); if (segment.getStr() != null) { @@ -44,7 +50,7 @@ public String evaluate(CapturedContext context, LogProbe.LogStatus status) { } else { if (parsedExr != null) { try { - Value result = parsedExr.execute(context); + Value result = parsedExr.execute(context, timeoutChecker); if (result.isUndefined()) { sb.append(result.getValue()); } else if (result.isNull()) { 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 3418ea1af4d..a6fd0cb5519 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 @@ -44,7 +44,6 @@ import de.thetaphi.forbiddenapis.SuppressForbidden; import java.io.IOException; import java.time.Duration; -import java.time.temporal.ChronoUnit; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; @@ -590,7 +589,9 @@ private boolean evaluateCondition(CapturedContext capture, LogStatus status) { return true; } try { - if (!probeCondition.execute(capture)) { + Duration timeout = Duration.ofMillis(Config.get().getDynamicInstrumentationEvalTimeout()); + TimeoutChecker timeoutChecker = TimeoutChecker.create(Config.get(), timeout); + if (!probeCondition.execute(capture, timeoutChecker)) { return false; } } catch (EvaluationException ex) { @@ -720,7 +721,9 @@ private void processCaptureExpressions(CapturedContext context, LogStatus logSta } for (CaptureExpression captureExpression : captureExpressions) { try { - Value result = captureExpression.expr.execute(context); + Duration timeout = Duration.ofMillis(Config.get().getDynamicInstrumentationEvalTimeout()); + TimeoutChecker timeoutChecker = TimeoutChecker.create(Config.get(), timeout); + Value result = captureExpression.expr.execute(context, timeoutChecker); if (result.isUndefined()) { throw new EvaluationException("UNDEFINED", captureExpression.getExpr().getDsl()); } @@ -837,9 +840,8 @@ public void commit(CapturedContext lineContext, int line) { if (isFullSnapshot()) { // freeze context just before commit because line probes have only one context Duration timeout = - Duration.of( - Config.get().getDynamicInstrumentationCaptureTimeout(), ChronoUnit.MILLIS); - lineContext.freeze(new TimeoutChecker(timeout)); + Duration.ofMillis(Config.get().getDynamicInstrumentationCaptureTimeout()); + lineContext.freeze(TimeoutChecker.create(Config.get(), timeout)); snapshot.addLine(lineContext, line); } commitSnapshot(snapshot, sink); 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 af9354429d1..d0c60012153 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 @@ -10,6 +10,7 @@ import com.datadog.debugger.instrumentation.InstrumentationResult; import com.datadog.debugger.instrumentation.MethodInfo; import com.datadog.debugger.sink.Snapshot; +import datadog.trace.api.Config; import datadog.trace.api.Pair; import datadog.trace.api.sampling.Sampler; import datadog.trace.bootstrap.debugger.CapturedContext; @@ -20,9 +21,11 @@ import datadog.trace.bootstrap.debugger.ProbeId; import datadog.trace.bootstrap.debugger.ProbeImplementation; import datadog.trace.bootstrap.debugger.ProbeRateLimiter; +import datadog.trace.bootstrap.debugger.util.TimeoutChecker; import datadog.trace.bootstrap.instrumentation.api.AgentSpan; import datadog.trace.bootstrap.instrumentation.api.AgentTracer; import datadog.trace.util.TagsHelper; +import java.time.Duration; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -206,10 +209,13 @@ public void evaluate( CapturedContext.Status status, MethodLocation methodLocation, boolean singleProbe) { + // Only one timeout for all conditions + Duration timeout = Duration.ofMillis(Config.get().getDynamicInstrumentationEvalTimeout()); + TimeoutChecker timeoutChecker = TimeoutChecker.create(Config.get(), timeout); for (Decoration decoration : decorations) { if (decoration.when != null) { try { - boolean condition = decoration.when.execute(context); + boolean condition = decoration.when.execute(context, timeoutChecker); if (!condition) { continue; } 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 f890df1d8e9..945b2a052dc 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 @@ -9,15 +9,18 @@ import com.datadog.debugger.instrumentation.DiagnosticMessage; import com.datadog.debugger.instrumentation.InstrumentationResult; import com.datadog.debugger.instrumentation.MethodInfo; +import datadog.trace.api.Config; import datadog.trace.api.sampling.Sampler; import datadog.trace.bootstrap.debugger.CapturedContext; import datadog.trace.bootstrap.debugger.CapturedContextProbe; import datadog.trace.bootstrap.debugger.MethodLocation; import datadog.trace.bootstrap.debugger.ProbeId; import datadog.trace.bootstrap.debugger.ProbeRateLimiter; +import datadog.trace.bootstrap.debugger.util.TimeoutChecker; import datadog.trace.bootstrap.instrumentation.api.AgentSpan; import datadog.trace.bootstrap.instrumentation.api.AgentTracer; import datadog.trace.bootstrap.instrumentation.api.Tags; +import java.time.Duration; import java.util.Arrays; import java.util.List; import java.util.Objects; @@ -134,7 +137,8 @@ private boolean evaluateCondition(CapturedContext capture) { } long start = System.nanoTime(); try { - return probeCondition.execute(capture); + Duration timeout = Duration.ofMillis(Config.get().getDynamicInstrumentationEvalTimeout()); + return probeCondition.execute(capture, TimeoutChecker.create(Config.get(), timeout)); } catch (Exception ex) { DebuggerAgent.getSink().getProbeStatusSink().addError(probeId, ex); return false; diff --git a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/util/MoshiSnapshotHelper.java b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/util/MoshiSnapshotHelper.java index 68cbb430c3e..b9aa67c28bf 100644 --- a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/util/MoshiSnapshotHelper.java +++ b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/util/MoshiSnapshotHelper.java @@ -21,7 +21,6 @@ import java.lang.reflect.Type; import java.nio.charset.StandardCharsets; import java.time.Duration; -import java.time.temporal.ChronoUnit; import java.util.List; import java.util.Map; import java.util.Set; @@ -118,8 +117,7 @@ public void toJson(JsonWriter jsonWriter, Snapshot.Captures captures) throws IOE jsonWriter.nullValue(); return; } - jsonWriter.setTag( - TimeoutChecker.class, new TimeoutChecker(System.currentTimeMillis(), captureTimeOut)); + jsonWriter.setTag(TimeoutChecker.class, TimeoutChecker.create(Config.get(), captureTimeOut)); jsonWriter.beginObject(); jsonWriter.name(ENTRY); capturedContextAdapter.toJson(jsonWriter, captures.getEntry()); @@ -160,12 +158,12 @@ public void toJson(JsonWriter jsonWriter, CapturedContext capturedContext) throw TimeoutChecker timeoutChecker = jsonWriter.tag(TimeoutChecker.class); if (timeoutChecker == null) { Duration timeout = - Duration.of(Config.get().getDynamicInstrumentationCaptureTimeout(), ChronoUnit.MILLIS); - timeoutChecker = new TimeoutChecker(timeout); + Duration.ofMillis(Config.get().getDynamicInstrumentationCaptureTimeout()); + timeoutChecker = TimeoutChecker.create(Config.get(), timeout); } // need to 'freeze' the context before serializing it capturedContext.freeze(timeoutChecker); - if (timeoutChecker.isTimedOut(System.currentTimeMillis())) { + if (timeoutChecker.isTimedOut()) { jsonWriter.beginObject(); jsonWriter.name(NOT_CAPTURED_REASON); jsonWriter.value(TIMEOUT_REASON); @@ -273,7 +271,7 @@ private SerializationResult toJsonCapturedValues( if (count >= limits.maxFieldCount) { return SerializationResult.FIELD_COUNT; } - if (timeoutChecker.isTimedOut(System.currentTimeMillis())) { + if (timeoutChecker.isTimedOut()) { return SerializationResult.TIMEOUT; } jsonWriter.name(entry.getKey()); @@ -305,7 +303,7 @@ public void toJson(JsonWriter jsonWriter, CapturedContext.CapturedValue captured } TimeoutChecker timeoutChecker = capturedValue.getTimeoutChecker(); if (timeoutChecker == null) { - timeoutChecker = new TimeoutChecker(Duration.of(10, ChronoUnit.MILLIS)); + timeoutChecker = TimeoutChecker.create(Config.get(), Duration.ofMillis(10)); } Object value = capturedValue.getValue(); String type = capturedValue.getType(); diff --git a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/util/SerializerWithLimits.java b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/util/SerializerWithLimits.java index 0ee03a7f52a..211543efa14 100644 --- a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/util/SerializerWithLimits.java +++ b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/util/SerializerWithLimits.java @@ -137,7 +137,7 @@ public void serialize(Object value, String type, Limits limits) throws Exception tokenWriter.epilogue(value); return; } - if (timeoutChecker.isTimedOut(System.currentTimeMillis())) { + if (timeoutChecker.isTimedOut()) { tokenWriter.notCaptured(NotCapturedReason.TIMEOUT); tokenWriter.epilogue(value); return; diff --git a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/util/ValueScriptHelper.java b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/util/ValueScriptHelper.java index bdbbacec81d..1ae9d5079d2 100644 --- a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/util/ValueScriptHelper.java +++ b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/util/ValueScriptHelper.java @@ -6,14 +6,12 @@ import datadog.trace.bootstrap.debugger.Limits; import datadog.trace.bootstrap.debugger.util.TimeoutChecker; import java.time.Duration; -import java.time.temporal.ChronoUnit; public class ValueScriptHelper { public static void serializeValue( StringBuilder sb, String expr, Object value, CapturedContext.Status status, Limits limits) { - Duration timeout = - Duration.of(Config.get().getDynamicInstrumentationCaptureTimeout(), ChronoUnit.MILLIS); - TimeoutChecker timeoutChecker = new TimeoutChecker(timeout); + Duration timeout = Duration.ofMillis(Config.get().getDynamicInstrumentationCaptureTimeout()); + TimeoutChecker timeoutChecker = TimeoutChecker.create(Config.get(), timeout); SerializerWithLimits serializer = new SerializerWithLimits(new StringTokenWriter(sb, status.getErrors()), timeoutChecker); try { 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 539a123b52d..0018c6d182b 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 @@ -31,6 +31,7 @@ import com.datadog.debugger.util.MoshiSnapshotTestHelper; import com.squareup.moshi.JsonAdapter; import datadog.environment.JavaVirtualMachine; +import datadog.trace.api.Config; import datadog.trace.bootstrap.debugger.CapturedContext; import datadog.trace.bootstrap.debugger.CapturedStackFrame; import datadog.trace.bootstrap.debugger.DebuggerContext; @@ -922,11 +923,9 @@ public void fieldCount20() throws IOException { @Test @Flaky public void timeOut() throws IOException { - DebuggerContext.initValueSerializer( - new TimeoutSnapshotSerializer(Duration.of(150, ChronoUnit.MILLIS))); + DebuggerContext.initValueSerializer(new TimeoutSnapshotSerializer(Duration.ofMillis(150))); JsonAdapter adapter = - MoshiHelper.createMoshiSnapshot(Duration.of(100, ChronoUnit.MILLIS)) - .adapter(Snapshot.class); + MoshiHelper.createMoshiSnapshot(Duration.ofMillis(100)).adapter(Snapshot.class); Snapshot snapshot = createSnapshot(); CapturedContext context = new CapturedContext(); CapturedContext.CapturedValue arg1 = CapturedContext.CapturedValue.of("arg1", "int", 42); @@ -949,7 +948,7 @@ public void valueTimeout() throws IOException { new TimeoutSnapshotSerializer(Duration.of(20, ChronoUnit.MILLIS))); CapturedContext.CapturedValue arg1 = CapturedContext.CapturedValue.of("arg1", Random.class.getTypeName(), new Random(0)); - arg1.freeze(new TimeoutChecker(Duration.ofMillis(10))); + arg1.freeze(TimeoutChecker.create(Config.get(), Duration.ofMillis(10))); String buffer = arg1.getStrValue(); System.out.println(buffer); Map json = MoshiHelper.createGenericAdapter().fromJson(buffer); diff --git a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/el/ELIntegrationSanityTest.java b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/el/ELIntegrationSanityTest.java index 9ac26df3239..7b6382cae21 100644 --- a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/el/ELIntegrationSanityTest.java +++ b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/el/ELIntegrationSanityTest.java @@ -4,6 +4,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import com.datadog.debugger.agent.JsonSnapshotSerializer; +import datadog.trace.api.Config; import datadog.trace.bootstrap.debugger.CapturedContext; import datadog.trace.bootstrap.debugger.DebuggerContext; import datadog.trace.bootstrap.debugger.Limits; @@ -59,12 +60,16 @@ void extractAfterEl() throws IllegalAccessException { capturedContext.addArguments(new CapturedContext.CapturedValue[] {thisValue}); // '.name.value' is not present in the snapshot - it needs to be retrieved via reflection - Value val = DSL.getMember(DSL.ref("name"), "value").evaluate(capturedContext); + Value val = + DSL.getMember(DSL.ref("name"), "value") + .evaluate( + new EvalContext( + capturedContext, TimeoutChecker.create(Config.get(), Duration.ofMillis(1000)))); // make sure the nested field was properly resolved assertEquals(p.name.value, val.getValue()); // freeze the captured context - capturedContext.freeze(new TimeoutChecker(Duration.of(1, ChronoUnit.SECONDS))); + capturedContext.freeze(TimeoutChecker.create(Config.get(), Duration.of(1, ChronoUnit.SECONDS))); // after freezing the original value is removed and only the serialized json representation // remains diff --git a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/util/StringTokenWriterTest.java b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/util/StringTokenWriterTest.java index 2480ca2004c..3009ff735d6 100644 --- a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/util/StringTokenWriterTest.java +++ b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/util/StringTokenWriterTest.java @@ -7,10 +7,10 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; +import datadog.trace.api.Config; import datadog.trace.bootstrap.debugger.Limits; import datadog.trace.bootstrap.debugger.util.TimeoutChecker; import java.time.Duration; -import java.time.temporal.ChronoUnit; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -175,7 +175,7 @@ private String serializeValue(Object value, Limits limits) throws Exception { SerializerWithLimits serializer = new SerializerWithLimits( new StringTokenWriter(sb, new ArrayList<>()), - new TimeoutChecker(Duration.of(300, ChronoUnit.SECONDS))); + TimeoutChecker.create(Config.get(), Duration.ofSeconds(300))); serializer.serialize( value, value != null ? value.getClass().getTypeName() : Object.class.getTypeName(), limits); return sb.toString(); diff --git a/dd-smoke-tests/debugger-integration-tests/src/main/java/datadog/smoketest/debugger/DebuggerTestApplication.java b/dd-smoke-tests/debugger-integration-tests/src/main/java/datadog/smoketest/debugger/DebuggerTestApplication.java index 2c1ffc19559..030167f6496 100644 --- a/dd-smoke-tests/debugger-integration-tests/src/main/java/datadog/smoketest/debugger/DebuggerTestApplication.java +++ b/dd-smoke-tests/debugger-integration-tests/src/main/java/datadog/smoketest/debugger/DebuggerTestApplication.java @@ -6,7 +6,9 @@ import java.lang.management.ManagementFactory; import java.lang.management.OperatingSystemMXBean; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.function.Consumer; @@ -51,6 +53,7 @@ private static void registerMethods() { methodsByName.put("multiProbesFullMethod", Main::runFullMethod); methodsByName.put("loopingFullMethod", Main::runLoopingFullMethod); methodsByName.put("exceptionMethod", Main::runExceptionMethod); + methodsByName.put("processLargeCollection", Main::runProcessLargeCollection); } private static void emptyMethod(String arg) {} @@ -82,6 +85,10 @@ private static void runExceptionMethod(String s) { exceptionMethod(s); } + private static void runProcessLargeCollection(String arg) { + processLargeCollection(1_000_000); + } + private static String fullMethod( int argInt, String argStr, double argDouble, Map argMap, String... argVar) { try { @@ -113,4 +120,14 @@ private static void exceptionMethod(String arg) { } } } + + private static void processLargeCollection(int largeListSize) { + List largeList = new ArrayList<>(); + for (int i = 0; i < largeListSize; i++) { + largeList.add("foobar" + i); + } + for (int i = 0; i < largeListSize; i++) { + largeList.get(i); + } + } } diff --git a/dd-smoke-tests/debugger-integration-tests/src/test/java/datadog/smoketest/LogProbesIntegrationTest.java b/dd-smoke-tests/debugger-integration-tests/src/test/java/datadog/smoketest/LogProbesIntegrationTest.java index 10c2bd38bd9..2d594af97e7 100644 --- a/dd-smoke-tests/debugger-integration-tests/src/test/java/datadog/smoketest/LogProbesIntegrationTest.java +++ b/dd-smoke-tests/debugger-integration-tests/src/test/java/datadog/smoketest/LogProbesIntegrationTest.java @@ -1,7 +1,9 @@ package datadog.smoketest; +import static com.datadog.debugger.el.DSL.all; import static com.datadog.debugger.el.DSL.and; import static com.datadog.debugger.el.DSL.eq; +import static com.datadog.debugger.el.DSL.filter; import static com.datadog.debugger.el.DSL.gt; import static com.datadog.debugger.el.DSL.len; import static com.datadog.debugger.el.DSL.nullValue; @@ -9,6 +11,7 @@ import static com.datadog.debugger.el.DSL.value; import static com.datadog.debugger.el.DSL.when; import static com.datadog.debugger.util.LogProbeTestHelper.parseTemplate; +import static java.util.Collections.singletonList; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -573,6 +576,74 @@ void testCaughtException() throws Exception { () -> String.format("timeout snapshotReceived=%s", snapshotReceived.get())); } + @Test + @DisplayName("testConditionTimeout") + void testConditionTimeout() throws Exception { + final String EXPECTED_UPLOADS = "4"; // 3 statuses + 1 snapshot + final String METHOD_NAME = "processLargeCollection"; + LogProbe probe = + LogProbe.builder() + .probeId(PROBE_ID) + .where(MAIN_CLASS_NAME, METHOD_NAME) + .evaluateAt(MethodLocation.EXIT) + .captureSnapshot(true) + .when( + new ProbeCondition( + when(all(ref("largeList"), gt(len(ref("@it")), value(0)))), + "all(largeList, {len(@it) > 0}")) + .build(); + setCurrentConfiguration(createConfig(probe)); + targetProcess = createProcessBuilder(logFilePath, METHOD_NAME, EXPECTED_UPLOADS).start(); + AtomicBoolean snapshotReceived = new AtomicBoolean(); + registerSnapshotListener( + snapshot -> { + if (snapshot.getProbe().getProbeId().equals(PROBE_ID)) { + assertEquals(1, snapshot.getEvaluationErrors().size()); + assertEquals("timeout (50ms)", snapshot.getEvaluationErrors().get(0).getMessage()); + snapshotReceived.set(true); + } + }); + processRequests( + snapshotReceived::get, + () -> String.format("timeout snapshotReceived=%s", snapshotReceived.get())); + } + + @Test + @DisplayName("testTemplateTimeout") + void testTemplateTimeout() throws Exception { + final String EXPECTED_UPLOADS = "4"; // 3 statuses + 1 snapshot + final String METHOD_NAME = "processLargeCollection"; + final String LARGE_COLLECTION_TEMPLATE = "{filter(largeList, {len(@it) > 0})}"; + List segments = + singletonList( + new LogProbe.Segment( + new ValueScript( + filter(ref("largeList"), gt(len(ref("@it")), value(0))), + "filter(largeList, {len(@it) > 0})"))); + LogProbe probe = + LogProbe.builder() + .probeId(PROBE_ID) + .where(MAIN_CLASS_NAME, METHOD_NAME) + .evaluateAt(MethodLocation.EXIT) + .captureSnapshot(true) + .template(LARGE_COLLECTION_TEMPLATE, segments) + .build(); + setCurrentConfiguration(createConfig(probe)); + targetProcess = createProcessBuilder(logFilePath, METHOD_NAME, EXPECTED_UPLOADS).start(); + AtomicBoolean snapshotReceived = new AtomicBoolean(); + registerSnapshotListener( + snapshot -> { + if (snapshot.getProbe().getProbeId().equals(PROBE_ID)) { + assertEquals(1, snapshot.getEvaluationErrors().size()); + assertEquals("timeout (50ms)", snapshot.getEvaluationErrors().get(0).getMessage()); + snapshotReceived.set(true); + } + }); + processRequests( + snapshotReceived::get, + () -> String.format("timeout snapshotReceived=%s", snapshotReceived.get())); + } + private ProbeId getProbeId(int i) { return new ProbeId(String.valueOf(i), 0); } diff --git a/dd-smoke-tests/debugger-integration-tests/src/test/java/datadog/smoketest/MoshiConfigTestHelper.java b/dd-smoke-tests/debugger-integration-tests/src/test/java/datadog/smoketest/MoshiConfigTestHelper.java index a19c5833212..23eb7f6fb17 100644 --- a/dd-smoke-tests/debugger-integration-tests/src/test/java/datadog/smoketest/MoshiConfigTestHelper.java +++ b/dd-smoke-tests/debugger-integration-tests/src/test/java/datadog/smoketest/MoshiConfigTestHelper.java @@ -153,7 +153,20 @@ public Void visit(FilterCollectionExpression filterCollectionExpression) { @Override public Void visit(HasAllExpression hasAllExpression) { - throw new UnsupportedOperationException("hasAll expression"); + try { + jsonWriter.beginObject(); + jsonWriter.name("all"); + jsonWriter.beginArray(); + hasAllExpression.getValueExpression().accept(this); + // jsonWriter.beginObject(); + hasAllExpression.getFilterPredicateExpression().accept(this); + // jsonWriter.endObject(); + jsonWriter.endArray(); + jsonWriter.endObject(); + } catch (IOException ex) { + LOGGER.debug("Cannot serialize: ", ex); + } + return null; } @Override diff --git a/dd-smoke-tests/debugger-integration-tests/src/test/java/datadog/smoketest/SpanDecorationProbesIntegrationTests.java b/dd-smoke-tests/debugger-integration-tests/src/test/java/datadog/smoketest/SpanDecorationProbesIntegrationTests.java index 93009c1a929..2537b676428 100644 --- a/dd-smoke-tests/debugger-integration-tests/src/test/java/datadog/smoketest/SpanDecorationProbesIntegrationTests.java +++ b/dd-smoke-tests/debugger-integration-tests/src/test/java/datadog/smoketest/SpanDecorationProbesIntegrationTests.java @@ -44,6 +44,8 @@ void setup(TestInfo testInfo) throws Exception { protected ProcessBuilder createProcessBuilder(Path logFilePath, String... params) { List commandParams = getDebuggerCommandParams(); commandParams.add("-Ddd.trace.enabled=true"); // explicitly enable tracer + // increase eval timeout for decoration evaluations + commandParams.add("-Ddd.dynamic.instrumentation.evaluation.timeout=100"); return ProcessBuilderHelper.createProcessBuilder( commandParams, logFilePath, getAppClass(), params); } diff --git a/dd-smoke-tests/debugger-integration-tests/src/test/java/datadog/smoketest/SpanProbesIntegrationTest.java b/dd-smoke-tests/debugger-integration-tests/src/test/java/datadog/smoketest/SpanProbesIntegrationTest.java index 3c55b8ff759..6cf6f17f883 100644 --- a/dd-smoke-tests/debugger-integration-tests/src/test/java/datadog/smoketest/SpanProbesIntegrationTest.java +++ b/dd-smoke-tests/debugger-integration-tests/src/test/java/datadog/smoketest/SpanProbesIntegrationTest.java @@ -60,7 +60,7 @@ void testLineRangeSpan() throws Exception { .probeId(PROBE_ID) // from line: System.out.println("fullMethod"); // to line: + String.join(",", argVar); - .where(MAIN_CLASS_NAME, 88, 97) + .where(MAIN_CLASS_NAME, 95, 104) .build(); setCurrentConfiguration(createSpanConfig(spanProbe)); targetProcess = createProcessBuilder(logFilePath, METHOD_NAME, EXPECTED_UPLOADS).start(); @@ -69,7 +69,7 @@ void testLineRangeSpan() throws Exception { registerTraceListener( decodedTrace -> { DecodedSpan decodedSpan = decodedTrace.getSpans().get(0); - assertEquals("Main.fullMethod:L88-97", decodedSpan.getResource()); + assertEquals("Main.fullMethod:L95-104", decodedSpan.getResource()); traceReceived.set(true); }); processRequests( @@ -92,7 +92,7 @@ void testSingleLineSpan() throws Exception { SpanProbe.builder() .probeId(PROBE_ID) // on line: System.out.println("fullMethod"); - .where(MAIN_CLASS_NAME, 88) + .where(MAIN_CLASS_NAME, 95) .build(); setCurrentConfiguration(createSpanConfig(spanProbe)); targetProcess = createProcessBuilder(logFilePath, METHOD_NAME, EXPECTED_UPLOADS).start(); 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 061698283a7..1aba2cb276b 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 @@ -231,7 +231,9 @@ public final class ConfigDefaults { static final int DEFAULT_DYNAMIC_INSTRUMENTATION_UPLOAD_BATCH_SIZE = 100; static final int DEFAULT_DYNAMIC_INSTRUMENTATION_MAX_PAYLOAD_SIZE = 1024; // KiB static final boolean DEFAULT_DYNAMIC_INSTRUMENTATION_VERIFY_BYTECODE = true; + static final String DEFAULT_DYNAMIC_INSTRUMENTATION_TIMEOUT_CHECKER_MODE = "WALL"; static final int DEFAULT_DYNAMIC_INSTRUMENTATION_CAPTURE_TIMEOUT = 100; // milliseconds + static final int DEFAULT_DYNAMIC_INSTRUMENTATION_EVAL_TIMEOUT = 50; // milliseconds static final int DEFAULT_DYNAMIC_INSTRUMENTATION_LOCALVAR_HOISTING_LEVEL = 1; static final boolean DEFAULT_SYMBOL_DATABASE_ENABLED = true; static final boolean DEFAULT_SYMBOL_DATABASE_FORCE_UPLOAD = false; 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 606267fa285..8af1e261f32 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,8 +32,12 @@ public final class DebuggerConfig { "dynamic.instrumentation.exclude.files"; public static final String DYNAMIC_INSTRUMENTATION_INCLUDE_FILES = "dynamic.instrumentation.include.files"; + public static final String DYNAMIC_INSTRUMENTATION_TIMEOUT_CHECKER_MODE = + "internal.dynamic.instrumentation.timeout.checker.mode"; public static final String DYNAMIC_INSTRUMENTATION_CAPTURE_TIMEOUT = "dynamic.instrumentation.capture.timeout"; + public static final String DYNAMIC_INSTRUMENTATION_EVAL_TIMEOUT = + "dynamic.instrumentation.evaluation.timeout"; public static final String DYNAMIC_INSTRUMENTATION_REDACTED_IDENTIFIERS = "dynamic.instrumentation.redacted.identifiers"; public static final String DYNAMIC_INSTRUMENTATION_REDACTION_EXCLUDED_IDENTIFIERS = 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 b10bda7c683..713be48c592 100644 --- a/internal-api/src/main/java/datadog/trace/api/Config.java +++ b/internal-api/src/main/java/datadog/trace/api/Config.java @@ -72,10 +72,12 @@ import static datadog.trace.api.ConfigDefaults.DEFAULT_DYNAMIC_INSTRUMENTATION_CLASSFILE_DUMP_ENABLED; import static datadog.trace.api.ConfigDefaults.DEFAULT_DYNAMIC_INSTRUMENTATION_DIAGNOSTICS_INTERVAL; import static datadog.trace.api.ConfigDefaults.DEFAULT_DYNAMIC_INSTRUMENTATION_ENABLED; +import static datadog.trace.api.ConfigDefaults.DEFAULT_DYNAMIC_INSTRUMENTATION_EVAL_TIMEOUT; import static datadog.trace.api.ConfigDefaults.DEFAULT_DYNAMIC_INSTRUMENTATION_LOCALVAR_HOISTING_LEVEL; import static datadog.trace.api.ConfigDefaults.DEFAULT_DYNAMIC_INSTRUMENTATION_MAX_PAYLOAD_SIZE; import static datadog.trace.api.ConfigDefaults.DEFAULT_DYNAMIC_INSTRUMENTATION_METRICS_ENABLED; import static datadog.trace.api.ConfigDefaults.DEFAULT_DYNAMIC_INSTRUMENTATION_POLL_INTERVAL; +import static datadog.trace.api.ConfigDefaults.DEFAULT_DYNAMIC_INSTRUMENTATION_TIMEOUT_CHECKER_MODE; import static datadog.trace.api.ConfigDefaults.DEFAULT_DYNAMIC_INSTRUMENTATION_UPLOAD_BATCH_SIZE; import static datadog.trace.api.ConfigDefaults.DEFAULT_DYNAMIC_INSTRUMENTATION_UPLOAD_FLUSH_INTERVAL; import static datadog.trace.api.ConfigDefaults.DEFAULT_DYNAMIC_INSTRUMENTATION_UPLOAD_TIMEOUT; @@ -328,6 +330,7 @@ import static datadog.trace.api.config.DebuggerConfig.DYNAMIC_INSTRUMENTATION_CLASSFILE_DUMP_ENABLED; import static datadog.trace.api.config.DebuggerConfig.DYNAMIC_INSTRUMENTATION_DIAGNOSTICS_INTERVAL; import static datadog.trace.api.config.DebuggerConfig.DYNAMIC_INSTRUMENTATION_ENABLED; +import static datadog.trace.api.config.DebuggerConfig.DYNAMIC_INSTRUMENTATION_EVAL_TIMEOUT; import static datadog.trace.api.config.DebuggerConfig.DYNAMIC_INSTRUMENTATION_EXCLUDE_FILES; import static datadog.trace.api.config.DebuggerConfig.DYNAMIC_INSTRUMENTATION_INCLUDE_FILES; import static datadog.trace.api.config.DebuggerConfig.DYNAMIC_INSTRUMENTATION_INSTRUMENT_THE_WORLD; @@ -340,6 +343,7 @@ import static datadog.trace.api.config.DebuggerConfig.DYNAMIC_INSTRUMENTATION_REDACTED_TYPES; import static datadog.trace.api.config.DebuggerConfig.DYNAMIC_INSTRUMENTATION_REDACTION_EXCLUDED_IDENTIFIERS; import static datadog.trace.api.config.DebuggerConfig.DYNAMIC_INSTRUMENTATION_SNAPSHOT_URL; +import static datadog.trace.api.config.DebuggerConfig.DYNAMIC_INSTRUMENTATION_TIMEOUT_CHECKER_MODE; import static datadog.trace.api.config.DebuggerConfig.DYNAMIC_INSTRUMENTATION_UPLOAD_BATCH_SIZE; import static datadog.trace.api.config.DebuggerConfig.DYNAMIC_INSTRUMENTATION_UPLOAD_FLUSH_INTERVAL; import static datadog.trace.api.config.DebuggerConfig.DYNAMIC_INSTRUMENTATION_UPLOAD_INTERVAL_SECONDS; @@ -1229,7 +1233,9 @@ public static String getHostName() { private final String dynamicInstrumentationInstrumentTheWorld; private final String dynamicInstrumentationExcludeFiles; private final String dynamicInstrumentationIncludeFiles; + private final String dynamicInstrumentationTimeoutCheckerMode; private final int dynamicInstrumentationCaptureTimeout; + private final int dynamicInstrumentationEvaluationTimeout; private final String dynamicInstrumentationRedactedIdentifiers; private final Set dynamicInstrumentationRedactionExcludedIdentifiers; private final String dynamicInstrumentationRedactedTypes; @@ -2886,10 +2892,17 @@ PROFILING_DATADOG_PROFILER_ENABLED, isDatadogProfilerSafeInCurrentEnvironment()) configProvider.getString(DYNAMIC_INSTRUMENTATION_EXCLUDE_FILES); dynamicInstrumentationIncludeFiles = configProvider.getString(DYNAMIC_INSTRUMENTATION_INCLUDE_FILES); + dynamicInstrumentationTimeoutCheckerMode = + configProvider.getString( + DYNAMIC_INSTRUMENTATION_TIMEOUT_CHECKER_MODE, + DEFAULT_DYNAMIC_INSTRUMENTATION_TIMEOUT_CHECKER_MODE); dynamicInstrumentationCaptureTimeout = configProvider.getInteger( DYNAMIC_INSTRUMENTATION_CAPTURE_TIMEOUT, DEFAULT_DYNAMIC_INSTRUMENTATION_CAPTURE_TIMEOUT); + dynamicInstrumentationEvaluationTimeout = + configProvider.getInteger( + DYNAMIC_INSTRUMENTATION_EVAL_TIMEOUT, DEFAULT_DYNAMIC_INSTRUMENTATION_EVAL_TIMEOUT); dynamicInstrumentationRedactedIdentifiers = configProvider.getString(DYNAMIC_INSTRUMENTATION_REDACTED_IDENTIFIERS, null); dynamicInstrumentationRedactionExcludedIdentifiers = @@ -4689,10 +4702,18 @@ public String getDynamicInstrumentationIncludeFiles() { return dynamicInstrumentationIncludeFiles; } + public String getDynamicInstrumentationTimeoutCheckerMode() { + return dynamicInstrumentationTimeoutCheckerMode; + } + public int getDynamicInstrumentationCaptureTimeout() { return dynamicInstrumentationCaptureTimeout; } + public int getDynamicInstrumentationEvalTimeout() { + return dynamicInstrumentationEvaluationTimeout; + } + public boolean isSymbolDatabaseEnabled() { return symbolDatabaseEnabled; } diff --git a/metadata/supported-configurations.json b/metadata/supported-configurations.json index ae081138b71..e90abb7921a 100644 --- a/metadata/supported-configurations.json +++ b/metadata/supported-configurations.json @@ -1217,6 +1217,14 @@ "aliases": ["DD_JMXFETCH_START_DELAY"] } ], + "DD_INTERNAL_DYNAMIC_INSTRUMENTATION_TIMEOUT_CHECKER_MODE": [ + { + "version": "A", + "type": "string", + "default": "WALL", + "aliases": [] + } + ], "DD_DYNAMIC_INSTRUMENTATION_CAPTURE_TIMEOUT": [ { "version": "A", @@ -1225,6 +1233,14 @@ "aliases": [] } ], + "DD_DYNAMIC_INSTRUMENTATION_EVALUATION_TIMEOUT": [ + { + "version": "A", + "type": "int", + "default": "50", + "aliases": [] + } + ], "DD_DYNAMIC_INSTRUMENTATION_CLASSFILE_DUMP_ENABLED": [ { "version": "A", From b500d56c2afc2a0c93a67f409e3865d0204ce217 Mon Sep 17 00:00:00 2001 From: jean-philippe bempel Date: Mon, 29 Jun 2026 15:10:05 +0200 Subject: [PATCH 2/5] fix CpuTimeoutChecker --- .../trace/bootstrap/debugger/util/CpuTimeoutChecker.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dd-java-agent/agent-debugger/debugger-bootstrap/src/main/java/datadog/trace/bootstrap/debugger/util/CpuTimeoutChecker.java b/dd-java-agent/agent-debugger/debugger-bootstrap/src/main/java/datadog/trace/bootstrap/debugger/util/CpuTimeoutChecker.java index 8ad1a3a111a..42d082ee675 100644 --- a/dd-java-agent/agent-debugger/debugger-bootstrap/src/main/java/datadog/trace/bootstrap/debugger/util/CpuTimeoutChecker.java +++ b/dd-java-agent/agent-debugger/debugger-bootstrap/src/main/java/datadog/trace/bootstrap/debugger/util/CpuTimeoutChecker.java @@ -21,6 +21,6 @@ public boolean isTimedOut() { @Override public Duration getTimeOut() { - return null; + return timeOut; } } From 255d479278a34e98c356b77b7a12897ed2ac6c86 Mon Sep 17 00:00:00 2001 From: jean-philippe bempel Date: Wed, 1 Jul 2026 15:00:35 +0200 Subject: [PATCH 3/5] fix config options --- .../smoketest/SpanDecorationProbesIntegrationTests.java | 2 +- .../java/datadog/trace/api/config/DebuggerConfig.java | 6 ++++-- internal-api/src/main/java/datadog/trace/api/Config.java | 8 +++++--- metadata/supported-configurations.json | 2 +- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/dd-smoke-tests/debugger-integration-tests/src/test/java/datadog/smoketest/SpanDecorationProbesIntegrationTests.java b/dd-smoke-tests/debugger-integration-tests/src/test/java/datadog/smoketest/SpanDecorationProbesIntegrationTests.java index 2537b676428..0785aac3a83 100644 --- a/dd-smoke-tests/debugger-integration-tests/src/test/java/datadog/smoketest/SpanDecorationProbesIntegrationTests.java +++ b/dd-smoke-tests/debugger-integration-tests/src/test/java/datadog/smoketest/SpanDecorationProbesIntegrationTests.java @@ -45,7 +45,7 @@ protected ProcessBuilder createProcessBuilder(Path logFilePath, String... params List commandParams = getDebuggerCommandParams(); commandParams.add("-Ddd.trace.enabled=true"); // explicitly enable tracer // increase eval timeout for decoration evaluations - commandParams.add("-Ddd.dynamic.instrumentation.evaluation.timeout=100"); + commandParams.add("-Ddd.dynamic.instrumentation.evaluation.timeout.ms=100"); return ProcessBuilderHelper.createProcessBuilder( commandParams, logFilePath, getAppClass(), params); } 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 8af1e261f32..1e9d90c2e43 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 @@ -36,8 +36,10 @@ public final class DebuggerConfig { "internal.dynamic.instrumentation.timeout.checker.mode"; public static final String DYNAMIC_INSTRUMENTATION_CAPTURE_TIMEOUT = "dynamic.instrumentation.capture.timeout"; - public static final String DYNAMIC_INSTRUMENTATION_EVAL_TIMEOUT = - "dynamic.instrumentation.evaluation.timeout"; + public static final String DYNAMIC_INSTRUMENTATION_CAPTURE_TIMEOUT_MS = + "dynamic.instrumentation.capture.timeout.ms"; + public static final String DYNAMIC_INSTRUMENTATION_EVAL_TIMEOUT_MS = + "dynamic.instrumentation.evaluation.timeout.ms"; public static final String DYNAMIC_INSTRUMENTATION_REDACTED_IDENTIFIERS = "dynamic.instrumentation.redacted.identifiers"; public static final String DYNAMIC_INSTRUMENTATION_REDACTION_EXCLUDED_IDENTIFIERS = 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 713be48c592..32e753d69fb 100644 --- a/internal-api/src/main/java/datadog/trace/api/Config.java +++ b/internal-api/src/main/java/datadog/trace/api/Config.java @@ -327,10 +327,11 @@ import static datadog.trace.api.config.DebuggerConfig.DEBUGGER_SOURCE_FILE_TRACKING_ENABLED; import static datadog.trace.api.config.DebuggerConfig.DISTRIBUTED_DEBUGGER_ENABLED; import static datadog.trace.api.config.DebuggerConfig.DYNAMIC_INSTRUMENTATION_CAPTURE_TIMEOUT; +import static datadog.trace.api.config.DebuggerConfig.DYNAMIC_INSTRUMENTATION_CAPTURE_TIMEOUT_MS; import static datadog.trace.api.config.DebuggerConfig.DYNAMIC_INSTRUMENTATION_CLASSFILE_DUMP_ENABLED; import static datadog.trace.api.config.DebuggerConfig.DYNAMIC_INSTRUMENTATION_DIAGNOSTICS_INTERVAL; import static datadog.trace.api.config.DebuggerConfig.DYNAMIC_INSTRUMENTATION_ENABLED; -import static datadog.trace.api.config.DebuggerConfig.DYNAMIC_INSTRUMENTATION_EVAL_TIMEOUT; +import static datadog.trace.api.config.DebuggerConfig.DYNAMIC_INSTRUMENTATION_EVAL_TIMEOUT_MS; import static datadog.trace.api.config.DebuggerConfig.DYNAMIC_INSTRUMENTATION_EXCLUDE_FILES; import static datadog.trace.api.config.DebuggerConfig.DYNAMIC_INSTRUMENTATION_INCLUDE_FILES; import static datadog.trace.api.config.DebuggerConfig.DYNAMIC_INSTRUMENTATION_INSTRUMENT_THE_WORLD; @@ -2899,10 +2900,11 @@ PROFILING_DATADOG_PROFILER_ENABLED, isDatadogProfilerSafeInCurrentEnvironment()) dynamicInstrumentationCaptureTimeout = configProvider.getInteger( DYNAMIC_INSTRUMENTATION_CAPTURE_TIMEOUT, - DEFAULT_DYNAMIC_INSTRUMENTATION_CAPTURE_TIMEOUT); + DEFAULT_DYNAMIC_INSTRUMENTATION_CAPTURE_TIMEOUT, + DYNAMIC_INSTRUMENTATION_CAPTURE_TIMEOUT_MS); dynamicInstrumentationEvaluationTimeout = configProvider.getInteger( - DYNAMIC_INSTRUMENTATION_EVAL_TIMEOUT, DEFAULT_DYNAMIC_INSTRUMENTATION_EVAL_TIMEOUT); + DYNAMIC_INSTRUMENTATION_EVAL_TIMEOUT_MS, DEFAULT_DYNAMIC_INSTRUMENTATION_EVAL_TIMEOUT); dynamicInstrumentationRedactedIdentifiers = configProvider.getString(DYNAMIC_INSTRUMENTATION_REDACTED_IDENTIFIERS, null); dynamicInstrumentationRedactionExcludedIdentifiers = diff --git a/metadata/supported-configurations.json b/metadata/supported-configurations.json index e90abb7921a..28cd6cb35dd 100644 --- a/metadata/supported-configurations.json +++ b/metadata/supported-configurations.json @@ -1233,7 +1233,7 @@ "aliases": [] } ], - "DD_DYNAMIC_INSTRUMENTATION_EVALUATION_TIMEOUT": [ + "DD_DYNAMIC_INSTRUMENTATION_EVALUATION_TIMEOUT_MS": [ { "version": "A", "type": "int", From b22a8402010014a5106bd9ea1aee6f2a044833b8 Mon Sep 17 00:00:00 2001 From: jean-philippe bempel Date: Thu, 2 Jul 2026 11:12:21 +0200 Subject: [PATCH 4/5] fix supported config --- metadata/supported-configurations.json | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/metadata/supported-configurations.json b/metadata/supported-configurations.json index 28cd6cb35dd..2138de1a2bf 100644 --- a/metadata/supported-configurations.json +++ b/metadata/supported-configurations.json @@ -1226,6 +1226,14 @@ } ], "DD_DYNAMIC_INSTRUMENTATION_CAPTURE_TIMEOUT": [ + { + "version": "A", + "type": "int", + "default": "100", + "aliases": ["DD_DYNAMIC_INSTRUMENTATION_CAPTURE_TIMEOUT_MS"] + } + ], + "DD_DYNAMIC_INSTRUMENTATION_CAPTURE_TIMEOUT_MS": [ { "version": "A", "type": "int", From 17c66699656156359ade3559f96fe4c75d7ef808 Mon Sep 17 00:00:00 2001 From: jean-philippe bempel Date: Thu, 2 Jul 2026 15:00:28 +0200 Subject: [PATCH 5/5] update supported config --- metadata/supported-configurations.json | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/metadata/supported-configurations.json b/metadata/supported-configurations.json index 2138de1a2bf..60dae10c020 100644 --- a/metadata/supported-configurations.json +++ b/metadata/supported-configurations.json @@ -1225,20 +1225,12 @@ "aliases": [] } ], - "DD_DYNAMIC_INSTRUMENTATION_CAPTURE_TIMEOUT": [ - { - "version": "A", - "type": "int", - "default": "100", - "aliases": ["DD_DYNAMIC_INSTRUMENTATION_CAPTURE_TIMEOUT_MS"] - } - ], "DD_DYNAMIC_INSTRUMENTATION_CAPTURE_TIMEOUT_MS": [ { - "version": "A", + "version": "B", "type": "int", "default": "100", - "aliases": [] + "aliases": ["DD_DYNAMIC_INSTRUMENTATION_CAPTURE_TIMEOUT"] } ], "DD_DYNAMIC_INSTRUMENTATION_EVALUATION_TIMEOUT_MS": [