diff --git a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/util/ClassNameFiltering.java b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/util/ClassNameFiltering.java index 87f65db8b4a..7722746d3da 100644 --- a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/util/ClassNameFiltering.java +++ b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/util/ClassNameFiltering.java @@ -6,9 +6,11 @@ import datadog.trace.util.ClassNameTrie; import java.util.Collections; import java.util.Set; +import java.util.regex.Pattern; /** A class to filter out classes based on their package name. */ public class ClassNameFiltering implements ClassNameFilter { + private static final Pattern LAMBDA_PROXY_CLASS_PATTERN = Pattern.compile(".*\\$\\$Lambda.*/.*"); private final ClassNameTrie includeTrie; private final ClassNameTrie excludeTrie; @@ -33,7 +35,12 @@ public ClassNameFiltering(Set excludes, Set includes) { } public boolean isExcluded(String className) { - return includeTrie.apply(className) < 0 && excludeTrie.apply(className) > 0; + return (includeTrie.apply(className) < 0 && excludeTrie.apply(className) > 0) + || isLambdaProxyClass(className); + } + + static boolean isLambdaProxyClass(String className) { + return LAMBDA_PROXY_CLASS_PATTERN.matcher(className).matches(); } public static ClassNameFiltering allowAll() { diff --git a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/util/ClassNameFilteringTest.java b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/util/ClassNameFilteringTest.java index fee1e0f5c57..bc5dec14c99 100644 --- a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/util/ClassNameFilteringTest.java +++ b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/util/ClassNameFilteringTest.java @@ -92,4 +92,25 @@ public void testExcludeDefaults(String input) { new ClassNameFiltering(ThirdPartyLibraries.INSTANCE.getThirdPartyLibraries(config)); assertTrue(classNameFiltering.isExcluded(input)); } + + @Test + void lambdaProxyClasses() { + // jdk8: at + // datadog.smoketest.debugger.ServerDebuggerTestApplication$$Lambda$231/1770027171.apply(:1000008) + // jdk11: at + // datadog.smoketest.debugger.ServerDebuggerTestApplication$$Lambda$262/0x0000000800467040.apply(Unknown Source) + // jdk17: at + // datadog.smoketest.debugger.ServerDebuggerTestApplication$$Lambda$303/0x00000008013dd1f8.apply(Unknown Source) + // jdk21: at + // datadog.smoketest.debugger.ServerDebuggerTestApplication$$Lambda/0x000000b801392c58.apply(Unknown Source) + assertTrue( + ClassNameFiltering.isLambdaProxyClass( + "datadog.smoketest.debugger.ServerDebuggerTestApplication$$Lambda$231/1770027171")); + assertTrue( + ClassNameFiltering.isLambdaProxyClass( + "datadog.smoketest.debugger.ServerDebuggerTestApplication$$Lambda$262/0x0000000800467040")); + assertTrue( + ClassNameFiltering.isLambdaProxyClass( + "at datadog.smoketest.debugger.ServerDebuggerTestApplication$$Lambda/0x000000b801392c58")); + } } diff --git a/dd-smoke-tests/debugger-integration-tests/src/main/java/datadog/smoketest/debugger/ServerDebuggerTestApplication.java b/dd-smoke-tests/debugger-integration-tests/src/main/java/datadog/smoketest/debugger/ServerDebuggerTestApplication.java index 717adf39155..821ef155a03 100644 --- a/dd-smoke-tests/debugger-integration-tests/src/main/java/datadog/smoketest/debugger/ServerDebuggerTestApplication.java +++ b/dd-smoke-tests/debugger-integration-tests/src/main/java/datadog/smoketest/debugger/ServerDebuggerTestApplication.java @@ -5,6 +5,7 @@ import java.util.HashMap; import java.util.Map; import java.util.function.Consumer; +import java.util.function.Function; import okhttp3.HttpUrl; import okhttp3.MediaType; import okhttp3.OkHttpClient; @@ -138,6 +139,8 @@ private static void runTracedMethod(String arg) { tracedMethodWithException(42, "foobar", 3.42, map, "var1", "var2", "var3"); } else if ("deepOops".equals(arg)) { tracedMethodWithDeepException1(42, "foobar", 3.42, map, "var1", "var2", "var3"); + } else if ("lambdaOops".equals(arg)) { + tracedMethodWithLambdaException(42, "foobar", 3.42, map, "var1", "var2", "var3"); } else { tracedMethod(42, "foobar", 3.42, map, "var1", "var2", "var3"); } @@ -215,6 +218,19 @@ private static void tracedMethodWithDeepException5( tracedMethodWithException(argInt, argStr, argDouble, argMap, argVar); } + private static void tracedMethodWithLambdaException( + int argInt, String argStr, double argDouble, Map argMap, String... argVar) { + throw toRuntimeException("lambdaOops"); + } + + private static RuntimeException toRuntimeException(String msg) { + return toException(RuntimeException::new, msg); + } + + private static S toException(Function constructor, String msg) { + return constructor.apply(msg); + } + private static class AppDispatcher extends Dispatcher { private final ServerDebuggerTestApplication app; diff --git a/dd-smoke-tests/debugger-integration-tests/src/test/java/datadog/smoketest/CodeOriginIntegrationTest.java b/dd-smoke-tests/debugger-integration-tests/src/test/java/datadog/smoketest/CodeOriginIntegrationTest.java index e228181a1ad..9c22dd34643 100644 --- a/dd-smoke-tests/debugger-integration-tests/src/test/java/datadog/smoketest/CodeOriginIntegrationTest.java +++ b/dd-smoke-tests/debugger-integration-tests/src/test/java/datadog/smoketest/CodeOriginIntegrationTest.java @@ -50,7 +50,7 @@ void testCodeOriginTraceAnnotation() throws Exception { assertEquals("runTracedMethod", span.getMeta().get(DD_CODE_ORIGIN_FRAMES_0_METHOD)); assertEquals( "(java.lang.String)", span.getMeta().get(DD_CODE_ORIGIN_FRAMES_0_SIGNATURE)); - assertEquals("133", span.getMeta().get(DD_CODE_ORIGIN_FRAMES_0_LINE)); + assertEquals("134", span.getMeta().get(DD_CODE_ORIGIN_FRAMES_0_LINE)); codeOrigin.set(true); } } diff --git a/dd-smoke-tests/debugger-integration-tests/src/test/java/datadog/smoketest/ExceptionDebuggerIntegrationTest.java b/dd-smoke-tests/debugger-integration-tests/src/test/java/datadog/smoketest/ExceptionDebuggerIntegrationTest.java index f2a7862aeca..3bd483c648a 100644 --- a/dd-smoke-tests/debugger-integration-tests/src/test/java/datadog/smoketest/ExceptionDebuggerIntegrationTest.java +++ b/dd-smoke-tests/debugger-integration-tests/src/test/java/datadog/smoketest/ExceptionDebuggerIntegrationTest.java @@ -237,6 +237,40 @@ void test5CapturedFrames() throws Exception { }); } + @Test + @DisplayName("testLambdaHiddenFrames") + @DisabledIf(value = "datadog.trace.api.Platform#isJ9", disabledReason = "HotSpot specific test") + void testLambdaHiddenFrames() throws Exception { + additionalJvmArgs.add("-XX:+UnlockDiagnosticVMOptions"); + additionalJvmArgs.add("-XX:+ShowHiddenFrames"); + appUrl = startAppAndAndGetUrl(); + execute(appUrl, TRACED_METHOD_NAME, "lambdaOops"); // instrumenting first exception + waitForInstrumentation(appUrl); + execute(appUrl, TRACED_METHOD_NAME, "lambdaOops"); // collecting snapshots and sending them + registerTraceListener(this::receiveExceptionReplayTrace); + registerSnapshotListener(this::receiveSnapshot); + processRequests( + () -> { + if (snapshotIdTags.isEmpty()) { + return false; + } + String snapshotId0 = snapshotIdTags.get(0); + if (traceReceived && snapshotReceived && snapshots.containsKey(snapshotId0)) { + Snapshot snapshot = snapshots.get(snapshotId0); + assertNotNull(snapshot); + assertEquals( + "lambdaOops", + snapshot.getCaptures().getReturn().getCapturedThrowable().getMessage()); + assertEquals( + "datadog.smoketest.debugger.ServerDebuggerTestApplication.tracedMethodWithLambdaException", + snapshot.getStack().get(0).getFunction()); + assertFullMethodCaptureArgs(snapshot.getCaptures().getReturn()); + return true; + } + return false; + }); + } + private void resetSnapshotsAndTraces() { resetTraceListener(); traceReceived = false;