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..42d082ee675 --- /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 timeOut; + } +} 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..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 @@ -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.ms=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..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 @@ -32,8 +32,14 @@ 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_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 b10bda7c683..32e753d69fb 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; @@ -325,9 +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_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; @@ -340,6 +344,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 +1234,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 +2893,18 @@ 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); + DEFAULT_DYNAMIC_INSTRUMENTATION_CAPTURE_TIMEOUT, + DYNAMIC_INSTRUMENTATION_CAPTURE_TIMEOUT_MS); + dynamicInstrumentationEvaluationTimeout = + configProvider.getInteger( + DYNAMIC_INSTRUMENTATION_EVAL_TIMEOUT_MS, DEFAULT_DYNAMIC_INSTRUMENTATION_EVAL_TIMEOUT); dynamicInstrumentationRedactedIdentifiers = configProvider.getString(DYNAMIC_INSTRUMENTATION_REDACTED_IDENTIFIERS, null); dynamicInstrumentationRedactionExcludedIdentifiers = @@ -4689,10 +4704,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..60dae10c020 100644 --- a/metadata/supported-configurations.json +++ b/metadata/supported-configurations.json @@ -1217,11 +1217,27 @@ "aliases": ["DD_JMXFETCH_START_DELAY"] } ], - "DD_DYNAMIC_INSTRUMENTATION_CAPTURE_TIMEOUT": [ + "DD_INTERNAL_DYNAMIC_INSTRUMENTATION_TIMEOUT_CHECKER_MODE": [ { "version": "A", + "type": "string", + "default": "WALL", + "aliases": [] + } + ], + "DD_DYNAMIC_INSTRUMENTATION_CAPTURE_TIMEOUT_MS": [ + { + "version": "B", "type": "int", "default": "100", + "aliases": ["DD_DYNAMIC_INSTRUMENTATION_CAPTURE_TIMEOUT"] + } + ], + "DD_DYNAMIC_INSTRUMENTATION_EVALUATION_TIMEOUT_MS": [ + { + "version": "A", + "type": "int", + "default": "50", "aliases": [] } ],