From 6405de349c467a43e72f5fe8014f75096a586809 Mon Sep 17 00:00:00 2001 From: Andrea Marziali Date: Mon, 7 Apr 2025 16:19:52 +0200 Subject: [PATCH 1/4] Use prefix trie for proxy ignores --- dd-java-agent/agent-tooling/build.gradle | 1 + .../agent/tooling/ProxyIgnoreBenchmark.java | 86 +++++++++++++++++++ .../bytebuddy/matcher/ProxyClassIgnores.java | 29 ++++--- .../matcher/proxy_ignored_class_name.trie | 29 +++++++ .../matcher/ProxyClassIgnoresTest.groovy | 20 +++++ 5 files changed, 155 insertions(+), 10 deletions(-) create mode 100644 dd-java-agent/agent-tooling/src/jmh/java/datadog/trace/agent/tooling/ProxyIgnoreBenchmark.java create mode 100644 dd-java-agent/agent-tooling/src/main/resources/datadog/trace/agent/tooling/bytebuddy/matcher/proxy_ignored_class_name.trie create mode 100644 dd-java-agent/agent-tooling/src/test/groovy/datadog/trace/agent/tooling/bytebuddy/matcher/ProxyClassIgnoresTest.groovy diff --git a/dd-java-agent/agent-tooling/build.gradle b/dd-java-agent/agent-tooling/build.gradle index 467202dc50e..31664e7357c 100644 --- a/dd-java-agent/agent-tooling/build.gradle +++ b/dd-java-agent/agent-tooling/build.gradle @@ -59,6 +59,7 @@ dependencies { testImplementation group: 'com.google.guava', name: 'guava-testlib', version: '20.0' jmhImplementation group: 'org.springframework.boot', name: 'spring-boot-starter-web', version: '2.3.5.RELEASE' + jmhAnnotationProcessor group: 'org.openjdk.jmh', name: 'jmh-generator-annprocess', version: '1.32' } jmh { diff --git a/dd-java-agent/agent-tooling/src/jmh/java/datadog/trace/agent/tooling/ProxyIgnoreBenchmark.java b/dd-java-agent/agent-tooling/src/jmh/java/datadog/trace/agent/tooling/ProxyIgnoreBenchmark.java new file mode 100644 index 00000000000..d248d21afbe --- /dev/null +++ b/dd-java-agent/agent-tooling/src/jmh/java/datadog/trace/agent/tooling/ProxyIgnoreBenchmark.java @@ -0,0 +1,86 @@ +package datadog.trace.agent.tooling; + +import datadog.trace.agent.tooling.bytebuddy.matcher.ProxyIgnoredClassNameTrie; +import java.util.concurrent.TimeUnit; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.infra.Blackhole; + +/* +Benchmark (className) Mode Cnt Score Error Units +ProxyIgnoreBenchmark.useContains org.springframework.util.ConcurrentReferenceHashMap$4 thrpt 2 0.007 ops/ns +ProxyIgnoreBenchmark.useContains java.lang.invoke.LambdaForm$DMH/0x00007fe9f0388000 thrpt 2 0.008 ops/ns +ProxyIgnoreBenchmark.useContains org.springframework.core.annotation.RepeatableContainers$StandardRepeatableContainers$$Lambda$315/0x00007fe9f03adc70 thrpt 2 0.003 ops/ns +ProxyIgnoreBenchmark.useContains datadog.test.package.redis.RedisTemplateProvider$$TestCGLIB$$FastClass$$0 thrpt 2 0.025 ops/ns +ProxyIgnoreBenchmark.useTrie org.springframework.util.ConcurrentReferenceHashMap$4 thrpt 2 0.026 ops/ns +ProxyIgnoreBenchmark.useTrie java.lang.invoke.LambdaForm$DMH/0x00007fe9f0388000 thrpt 2 0.026 ops/ns +ProxyIgnoreBenchmark.useTrie org.springframework.core.annotation.RepeatableContainers$StandardRepeatableContainers$$Lambda$315/0x00007fe9f03adc70 thrpt 2 0.009 ops/ns +ProxyIgnoreBenchmark.useTrie datadog.test.package.redis.RedisTemplateProvider$$TestCGLIB$$FastClass$$0 thrpt 2 0.029 ops/ns +*/ +@State(Scope.Benchmark) +@BenchmarkMode(Mode.Throughput) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@Measurement(iterations = 2, time = 5, timeUnit = TimeUnit.SECONDS) +@Warmup(iterations = 1) +@Fork(value = 1) +public class ProxyIgnoreBenchmark { + @Param({ + "org.springframework.util.ConcurrentReferenceHashMap$4", + "java.lang.invoke.LambdaForm$DMH/0x00007fe9f0388000", + "org.springframework.core.annotation.RepeatableContainers$StandardRepeatableContainers$$Lambda$315/0x00007fe9f03adc70", + // worst case for trie based + "datadog.test.package.redis.RedisTemplateProvider$$TestCGLIB$$FastClass$$0" + }) + public String className; + + @Benchmark + public void useContains(Blackhole bh) { + if (className.indexOf('$') > -1) { + if (className.contains("$JaxbAccessor") + || className.contains("CGLIB$$") + || className.contains("$__sisu") + || className.contains("$$EnhancerByGuice$$") + || className.contains("$$EnhancerByProxool$$") + || className.contains("$$$view") + || className.contains("$$$endpoint") // jboss mdb proxies + || className.contains("$$_Weld") + || className.contains("_$$_jvst")) { + bh.consume(true); + } + } + } + + @Benchmark + public void useTrie(Blackhole bh) { + int last = -1; + int idx; + while (true) { + idx = className.indexOf('$', last + 1); + if (idx < 0) { + break; + } + if (last < 0 && className.contains("CGLIB$$")) { + break; + } + if (idx == last + 1) { + // skip the trie if consecutive $$ since, to be efficient, we can match prefixes from the + // first dollar + last = idx; + continue; + } + last = idx; + if (ProxyIgnoredClassNameTrie.apply(className, idx) >= 0) { + return; + } + } + bh.consume(true); + } +} diff --git a/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/bytebuddy/matcher/ProxyClassIgnores.java b/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/bytebuddy/matcher/ProxyClassIgnores.java index 041ba7198fe..dd85e576d40 100644 --- a/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/bytebuddy/matcher/ProxyClassIgnores.java +++ b/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/bytebuddy/matcher/ProxyClassIgnores.java @@ -5,16 +5,25 @@ public class ProxyClassIgnores { private ProxyClassIgnores() {} public static boolean isIgnored(String name) { - if (name.indexOf('$') > -1) { - if (name.contains("$JaxbAccessor") - || name.contains("CGLIB$$") - || name.contains("$__sisu") - || name.contains("$$EnhancerByGuice$$") - || name.contains("$$EnhancerByProxool$$") - || name.contains("$$$view") - || name.contains("$$$endpoint") // jboss mdb proxies - || name.contains("$$_Weld") - || name.contains("_$$_jvst")) { + int last = -1; + int idx; + while (true) { + idx = name.indexOf('$', last + 1); + if (idx < 0) { + break; + } + if (last < 0 && name.contains("CGLIB$$")) { + // check this once + return true; + } + if (idx == last + 1) { + // skip the trie if consecutive $$ since, to be efficient, we can match prefixes from the + // first dollar + last = idx; + continue; + } + last = idx; + if (ProxyIgnoredClassNameTrie.apply(name, idx) == 1) { return true; } } diff --git a/dd-java-agent/agent-tooling/src/main/resources/datadog/trace/agent/tooling/bytebuddy/matcher/proxy_ignored_class_name.trie b/dd-java-agent/agent-tooling/src/main/resources/datadog/trace/agent/tooling/bytebuddy/matcher/proxy_ignored_class_name.trie new file mode 100644 index 00000000000..a7edc45f637 --- /dev/null +++ b/dd-java-agent/agent-tooling/src/main/resources/datadog/trace/agent/tooling/bytebuddy/matcher/proxy_ignored_class_name.trie @@ -0,0 +1,29 @@ +# Generates 'IgnoredClassNameTrie.java' + +# This file lists classes that are considered as proxy hence ignored. +# The trie will be matched starting from the first '$' char included. +# Use 0 to allow transformation of packages or classes beneath ignored packages +# Use 1 to ignore. +# End lines with '*' to match any trailing char if needed. + +# 0 = global allows +# 1 = system-level ignores + +# --------- ALLOW SHORTCUTS ----------- + +# lambda factory generated classes are not proxies +0 $$Lambda$* + +# -------- SYSTEM-LEVEL IGNORES -------- + +1 $$weld* +1 $JaxbAccessor* +1 $__sisu* +1 $$EnhancerByGuice$$* +1 $$EnhancerByProxool$$* +1 $$$view* +# jboss mdb proxies +1 $$$endpoint* +1 $$_Weld* +1 $$_jvst* +1 $$SpringCGLIB$$* diff --git a/dd-java-agent/agent-tooling/src/test/groovy/datadog/trace/agent/tooling/bytebuddy/matcher/ProxyClassIgnoresTest.groovy b/dd-java-agent/agent-tooling/src/test/groovy/datadog/trace/agent/tooling/bytebuddy/matcher/ProxyClassIgnoresTest.groovy new file mode 100644 index 00000000000..c58dc73cc5c --- /dev/null +++ b/dd-java-agent/agent-tooling/src/test/groovy/datadog/trace/agent/tooling/bytebuddy/matcher/ProxyClassIgnoresTest.groovy @@ -0,0 +1,20 @@ +package datadog.trace.agent.tooling.bytebuddy.matcher + +import datadog.trace.test.util.DDSpecification + +class ProxyClassIgnoresTest extends DDSpecification { + def 'test #clsName exclusion'() { + given: + def result = ProxyClassIgnores.isIgnored(clsName) + expect: + result == excluded + where: + clsName | excluded + 'org.springframework.util.ConcurrentReferenceHashMap$4' | false + 'io.github.resilience4j.springboot3.circuitbreaker.autoconfigure.CircuitBreakerAutoConfiguration$CircuitBreakerEndpointAutoConfiguration$$SpringCGLIB$$0' | true + 'io.micrometer.core.instrument.config.NamingConvention$$Lambda$1415' | false + 'com.package.name.Class$JaxbAccessorF_variablename' | true + 'my.package.Resource$Proxy$_$$_Weld$EnterpriseProxy' | true + 'com.abc.MyResourceImpl$$EnhancerByGuice$$69175a50' | true + } +} From e095d9cf7f3fddf32995a4d8ba6952846b43b562 Mon Sep 17 00:00:00 2001 From: Andrea Marziali Date: Mon, 28 Apr 2025 09:29:24 +0200 Subject: [PATCH 2/4] Update dd-java-agent/agent-tooling/src/main/resources/datadog/trace/agent/tooling/bytebuddy/matcher/proxy_ignored_class_name.trie Co-authored-by: Stuart McCulloch --- .../tooling/bytebuddy/matcher/proxy_ignored_class_name.trie | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dd-java-agent/agent-tooling/src/main/resources/datadog/trace/agent/tooling/bytebuddy/matcher/proxy_ignored_class_name.trie b/dd-java-agent/agent-tooling/src/main/resources/datadog/trace/agent/tooling/bytebuddy/matcher/proxy_ignored_class_name.trie index a7edc45f637..c1956be49c7 100644 --- a/dd-java-agent/agent-tooling/src/main/resources/datadog/trace/agent/tooling/bytebuddy/matcher/proxy_ignored_class_name.trie +++ b/dd-java-agent/agent-tooling/src/main/resources/datadog/trace/agent/tooling/bytebuddy/matcher/proxy_ignored_class_name.trie @@ -1,4 +1,4 @@ -# Generates 'IgnoredClassNameTrie.java' +# Generates 'ProxyIgnoredClassNameTrie.java' # This file lists classes that are considered as proxy hence ignored. # The trie will be matched starting from the first '$' char included. From 444f285d783535d3246e097de6d3ddd801e00356 Mon Sep 17 00:00:00 2001 From: Andrea Marziali Date: Mon, 28 Apr 2025 14:47:54 +0200 Subject: [PATCH 3/4] Add suggestion from Bruce --- .../tooling/bytebuddy/matcher/ProxyClassIgnores.java | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/bytebuddy/matcher/ProxyClassIgnores.java b/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/bytebuddy/matcher/ProxyClassIgnores.java index dd85e576d40..30c00e5af37 100644 --- a/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/bytebuddy/matcher/ProxyClassIgnores.java +++ b/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/bytebuddy/matcher/ProxyClassIgnores.java @@ -5,13 +5,7 @@ public class ProxyClassIgnores { private ProxyClassIgnores() {} public static boolean isIgnored(String name) { - int last = -1; - int idx; - while (true) { - idx = name.indexOf('$', last + 1); - if (idx < 0) { - break; - } + for (int last = -1, idx; (idx = name.indexOf('$', last + 1)) >= 0; last = idx) { if (last < 0 && name.contains("CGLIB$$")) { // check this once return true; @@ -19,10 +13,8 @@ public static boolean isIgnored(String name) { if (idx == last + 1) { // skip the trie if consecutive $$ since, to be efficient, we can match prefixes from the // first dollar - last = idx; continue; } - last = idx; if (ProxyIgnoredClassNameTrie.apply(name, idx) == 1) { return true; } From 39372682bfcbbb66b93daffabceeee7f4b67a71a Mon Sep 17 00:00:00 2001 From: Andrea Marziali Date: Mon, 28 Apr 2025 15:03:22 +0200 Subject: [PATCH 4/4] remove annotation processor --- dd-java-agent/agent-tooling/build.gradle | 1 - 1 file changed, 1 deletion(-) diff --git a/dd-java-agent/agent-tooling/build.gradle b/dd-java-agent/agent-tooling/build.gradle index 31664e7357c..467202dc50e 100644 --- a/dd-java-agent/agent-tooling/build.gradle +++ b/dd-java-agent/agent-tooling/build.gradle @@ -59,7 +59,6 @@ dependencies { testImplementation group: 'com.google.guava', name: 'guava-testlib', version: '20.0' jmhImplementation group: 'org.springframework.boot', name: 'spring-boot-starter-web', version: '2.3.5.RELEASE' - jmhAnnotationProcessor group: 'org.openjdk.jmh', name: 'jmh-generator-annprocess', version: '1.32' } jmh {