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..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,16 +5,17 @@ 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")) { + for (int last = -1, idx; (idx = name.indexOf('$', last + 1)) >= 0; last = idx) { + 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 + continue; + } + 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..c1956be49c7 --- /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 'ProxyIgnoredClassNameTrie.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 + } +}