diff --git a/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/bytebuddy/ClassCodeFilter.java b/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/bytebuddy/ClassCodeFilter.java
deleted file mode 100644
index 5f9e5c7f86b..00000000000
--- a/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/bytebuddy/ClassCodeFilter.java
+++ /dev/null
@@ -1,85 +0,0 @@
-package datadog.trace.agent.tooling.bytebuddy;
-
-import java.util.Arrays;
-
-/**
- * Compact filter that records class membership by their hash and short 'class-code'.
- *
- *
The 'class-code' includes the length of the package prefix and simple name, as well as the
- * first and last characters of the simple name. These elements coupled with the hash of the full
- * class name should minimize the probability of collisions without needing to store full names,
- * which would otherwise make the filter overly large.
- */
-public abstract class ClassCodeFilter {
-
- private static final int MAX_CAPACITY = 1 << 16;
- private static final int MIN_CAPACITY = 1 << 8;
- private static final int MAX_HASH_ATTEMPTS = 3;
-
- protected final long[] slots;
- protected final int slotMask;
-
- protected ClassCodeFilter(int capacity) {
- if (capacity < MIN_CAPACITY) {
- capacity = MIN_CAPACITY;
- } else if (capacity > MAX_CAPACITY) {
- capacity = MAX_CAPACITY;
- }
-
- // choose enough slot bits to cover the chosen capacity
- slotMask = 0xFFFFFFFF >>> Integer.numberOfLeadingZeros(capacity - 1);
- slots = new long[slotMask + 1];
- }
-
- public final boolean contains(String name) {
- int hash = name.hashCode();
- for (int i = 1, h = hash; true; i++) {
- long value = slots[slotMask & h];
- if (value == 0) {
- return false;
- } else if ((int) value == hash) {
- return (int) (value >>> 32) == classCode(name);
- } else if (i == MAX_HASH_ATTEMPTS) {
- return false;
- }
- h = rehash(h);
- }
- }
-
- public final void add(String name) {
- int index;
- int hash = name.hashCode();
- for (int i = 1, h = hash; true; i++) {
- index = slotMask & h;
- if (slots[index] == 0) {
- break;
- } else if (i == MAX_HASH_ATTEMPTS) {
- index = slotMask & hash; // overwrite original slot
- break;
- }
- h = rehash(h);
- }
- slots[index] = (long) classCode(name) << 32 | 0xFFFFFFFFL & hash;
- }
-
- public final void clear() {
- Arrays.fill(slots, 0);
- }
-
- /**
- * Computes a 32-bit 'class-code' that includes the length of the package prefix and simple name,
- * plus the first and last characters of the simple name (each truncated to fit into 8-bits.)
- */
- private static int classCode(String name) {
- int start = name.lastIndexOf('.') + 1;
- int end = name.length() - 1;
- int code = 0xFF & start;
- code = (code << 8) | (0xFF & name.charAt(start));
- code = (code << 8) | (0xFF & name.charAt(end));
- return (code << 8) | (0xFF & (end - start));
- }
-
- private static int rehash(int oldHash) {
- return Integer.reverseBytes(oldHash * 0x9e3775cd) * 0x9e3775cd;
- }
-}
diff --git a/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/bytebuddy/memoize/Memoizer.java b/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/bytebuddy/memoize/Memoizer.java
index 604912dabce..1ffb277d4c4 100644
--- a/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/bytebuddy/memoize/Memoizer.java
+++ b/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/bytebuddy/memoize/Memoizer.java
@@ -2,6 +2,7 @@
import static net.bytebuddy.matcher.ElementMatchers.not;
+import datadog.instrument.utils.ClassNameFilter;
import datadog.trace.agent.tooling.InstrumenterMetrics;
import datadog.trace.agent.tooling.bytebuddy.TypeInfoCache;
import datadog.trace.agent.tooling.bytebuddy.TypeInfoCache.SharedTypeInfo;
@@ -61,7 +62,7 @@ enum MatcherKind {
private static final boolean namesAreUnique = InstrumenterConfig.get().isResolverNamesAreUnique();
// compact filter recording uninteresting types
- private static final NoMatchFilter noMatchFilter = new NoMatchFilter();
+ private static final ClassNameFilter noMatchFilter = NoMatchFilter.build();
// caches positive memoized matches
private static final TypeInfoCache memos =
diff --git a/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/bytebuddy/memoize/NoMatchFilter.java b/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/bytebuddy/memoize/NoMatchFilter.java
index a4f08d2edf2..ca87baa876d 100644
--- a/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/bytebuddy/memoize/NoMatchFilter.java
+++ b/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/bytebuddy/memoize/NoMatchFilter.java
@@ -2,7 +2,7 @@
import static datadog.trace.util.AgentThreadFactory.AGENT_THREAD_GROUP;
-import datadog.trace.agent.tooling.bytebuddy.ClassCodeFilter;
+import datadog.instrument.utils.ClassNameFilter;
import datadog.trace.api.Config;
import datadog.trace.api.DDTraceApiInfo;
import datadog.trace.api.InstrumenterConfig;
@@ -19,21 +19,37 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-/** Compact filter that records uninteresting types. */
-final class NoMatchFilter extends ClassCodeFilter {
+/** Builds a persistable compact filter that records uninteresting types. */
+final class NoMatchFilter {
private static final Logger log = LoggerFactory.getLogger(NoMatchFilter.class);
- NoMatchFilter() {
- super(InstrumenterConfig.get().getResolverNoMatchesSize());
+ private static final String TRACER_VERSION_HEADER = "dd-java-agent";
+ private static final String NO_MATCH_FILTER_HEADER = "NoMatchFilter";
- // seed filter from previously collected results?
+ private NoMatchFilter() {}
+
+ public static ClassNameFilter build() {
Path noMatchFile = discoverNoMatchFile();
if (null != noMatchFile) {
- seedNoMatchFilter(noMatchFile);
+ // support persisting/restoring no-match results
+ try {
+ if (Files.exists(noMatchFile)) {
+ // restore existing filter with previously collected results
+ return seedNoMatchFilter(noMatchFile);
+ } else {
+ // populate filter from current run and persist at shutdown
+ ClassNameFilter filter = emptyNoMatchFilter();
+ Runtime.getRuntime().addShutdownHook(new ShutdownHook(noMatchFile, filter));
+ return filter;
+ }
+ } catch (Throwable e) {
+ log.debug("Unable to use NoMatchFilter at {}", noMatchFile, e);
+ }
}
+ return emptyNoMatchFilter();
}
- static Path discoverNoMatchFile() {
+ private static Path discoverNoMatchFile() {
String cacheDir = InstrumenterConfig.get().getResolverCacheDir();
if (null == cacheDir) {
return null;
@@ -53,51 +69,49 @@ static Path discoverNoMatchFile() {
return Paths.get(cacheDir, noMatchFilterName);
}
- void seedNoMatchFilter(Path noMatchFile) {
- if (!Files.exists(noMatchFile)) {
- Runtime.getRuntime().addShutdownHook(new ShutdownHook(noMatchFile));
- } else {
- log.debug("Seeding NoMatchFilter from {}", noMatchFile);
- try (DataInputStream in =
- new DataInputStream(new BufferedInputStream(Files.newInputStream(noMatchFile)))) {
- while (true) {
- switch (in.readUTF()) {
- case "dd-java-agent":
- expectVersion(in, DDTraceApiInfo.VERSION);
- break;
- case "NoMatchFilter":
- if (in.readInt() != slots.length) {
- throw new IOException("filter size mismatch");
- }
- for (int i = 0; i < slots.length; i++) {
- slots[i] = in.readLong();
- }
- return;
- default:
- throw new IOException("unexpected content");
- }
- }
- } catch (IOException e) {
- if (log.isDebugEnabled()) {
- log.info("Unable to seed NoMatchFilter from {}", noMatchFile, e);
- } else {
- log.info("Unable to seed NoMatchFilter from {}: {}", noMatchFile, e.getMessage());
+ private static ClassNameFilter emptyNoMatchFilter() {
+ return new ClassNameFilter(InstrumenterConfig.get().getResolverNoMatchesSize());
+ }
+
+ private static ClassNameFilter seedNoMatchFilter(Path noMatchFile) {
+ log.debug("Seeding NoMatchFilter from {}", noMatchFile);
+ try (DataInputStream in =
+ new DataInputStream(new BufferedInputStream(Files.newInputStream(noMatchFile)))) {
+ while (true) {
+ switch (in.readUTF()) {
+ case TRACER_VERSION_HEADER:
+ expectVersion(in, DDTraceApiInfo.VERSION);
+ break;
+ case NO_MATCH_FILTER_HEADER:
+ return ClassNameFilter.readFrom(in);
+ default:
+ throw new IOException("unexpected content");
}
}
+ } catch (Throwable e) {
+ if (log.isDebugEnabled()) {
+ log.info("Unable to seed NoMatchFilter from {}", noMatchFile, e);
+ } else {
+ log.info("Unable to seed NoMatchFilter from {}: {}", noMatchFile, e.getMessage());
+ }
+ return emptyNoMatchFilter();
}
}
- void persistNoMatchFilter(Path noMatchFile) {
+ private static void expectVersion(DataInputStream in, String version) throws IOException {
+ if (!version.equals(in.readUTF())) {
+ throw new IOException("version mismatch");
+ }
+ }
+
+ static void persistNoMatchFilter(Path noMatchFile, ClassNameFilter filter) {
log.debug("Persisting NoMatchFilter to {}", noMatchFile);
try (DataOutputStream out =
new DataOutputStream(new BufferedOutputStream(Files.newOutputStream(noMatchFile)))) {
- out.writeUTF("dd-java-agent");
+ out.writeUTF(TRACER_VERSION_HEADER);
out.writeUTF(DDTraceApiInfo.VERSION);
- out.writeUTF("NoMatchFilter");
- out.writeInt(slots.length);
- for (long slot : slots) {
- out.writeLong(slot);
- }
+ out.writeUTF(NO_MATCH_FILTER_HEADER);
+ filter.writeTo(out);
} catch (IOException e) {
if (log.isDebugEnabled()) {
log.info("Unable to persist NoMatchFilter to {}", noMatchFile, e);
@@ -107,23 +121,19 @@ void persistNoMatchFilter(Path noMatchFile) {
}
}
- static void expectVersion(DataInputStream in, String version) throws IOException {
- if (!version.equals(in.readUTF())) {
- throw new IOException("version mismatch");
- }
- }
-
- class ShutdownHook extends Thread {
+ private static final class ShutdownHook extends Thread {
private final Path noMatchFile;
+ private final ClassNameFilter filter;
- ShutdownHook(Path noMatchFile) {
+ ShutdownHook(Path noMatchFile, ClassNameFilter filter) {
super(AGENT_THREAD_GROUP, "dd-NoMatchFilter-persist-hook");
this.noMatchFile = noMatchFile;
+ this.filter = filter;
}
@Override
public void run() {
- persistNoMatchFilter(noMatchFile);
+ persistNoMatchFilter(noMatchFile, filter);
}
}
}
diff --git a/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/bytebuddy/outline/IsPublicFilter.java b/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/bytebuddy/outline/IsPublicFilter.java
deleted file mode 100644
index 026d4beea6b..00000000000
--- a/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/bytebuddy/outline/IsPublicFilter.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package datadog.trace.agent.tooling.bytebuddy.outline;
-
-import datadog.trace.agent.tooling.bytebuddy.ClassCodeFilter;
-import datadog.trace.api.InstrumenterConfig;
-
-/** Compact filter that records public types. */
-final class IsPublicFilter extends ClassCodeFilter {
- IsPublicFilter() {
- super(InstrumenterConfig.get().getResolverVisibilitySize());
- }
-}
diff --git a/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/bytebuddy/outline/TypeFactory.java b/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/bytebuddy/outline/TypeFactory.java
index ba9b29438af..162f3d17555 100644
--- a/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/bytebuddy/outline/TypeFactory.java
+++ b/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/bytebuddy/outline/TypeFactory.java
@@ -5,6 +5,7 @@
import static datadog.trace.bootstrap.AgentClassLoading.LOCATING_CLASS;
import static net.bytebuddy.dynamic.loading.ClassLoadingStrategy.BOOTSTRAP_LOADER;
+import datadog.instrument.utils.ClassNameFilter;
import datadog.trace.agent.tooling.InstrumenterMetrics;
import datadog.trace.agent.tooling.bytebuddy.ClassFileLocators;
import datadog.trace.agent.tooling.bytebuddy.TypeInfoCache;
@@ -86,7 +87,8 @@ final class TypeFactory {
private static final TypeInfoCache fullTypes =
new TypeInfoCache<>(InstrumenterConfig.get().getResolverTypePoolSize());
- static final IsPublicFilter isPublicFilter = new IsPublicFilter();
+ static final ClassNameFilter isPublicFilter =
+ new ClassNameFilter(InstrumenterConfig.get().getResolverVisibilitySize());
/** Small local cache to help deduplicate lookups when matching/transforming. */
private final DDCache deferredTypes = DDCaches.newFixedSizeCache(16);