Skip to content

Commit 9dfd920

Browse files
authored
Support filtering shared class-info by arbitrary predicates (#16)
For example, filtering by class-loader sub-graphs
1 parent a53b253 commit 9dfd920

File tree

2 files changed

+80
-0
lines changed

2 files changed

+80
-0
lines changed

utils/src/main/java/datadog/instrument/utils/ClassInfoCache.java

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
import java.util.Arrays;
1010
import java.util.concurrent.atomic.AtomicLong;
11+
import java.util.function.IntPredicate;
1112

1213
/**
1314
* Shares class information from multiple classloaders in a single cache.
@@ -132,6 +133,47 @@ public T find(CharSequence className, int classLoaderKeyId) {
132133
}
133134
}
134135

136+
/**
137+
* Finds information for the given class-name, filtered by class-loader key.
138+
*
139+
* @param className the class-name
140+
* @param classLoaderKeyFilter the filter for the class-loader key
141+
* @return information shared under the class-name and filtered class-loader
142+
* @see ClassLoaderIndex#getClassLoaderKeyId(ClassLoader)
143+
*/
144+
@SuppressWarnings("unchecked")
145+
public T find(CharSequence className, IntPredicate classLoaderKeyFilter) {
146+
final int hash = className.hashCode();
147+
final SharedInfo[] shared = this.shared;
148+
final int slotMask = this.slotMask;
149+
150+
// try to find matching slot, rehashing after each attempt
151+
for (int i = 1, h = hash; true; i++, h = rehash(h)) {
152+
int slot = slotMask & h;
153+
SharedInfo existing = shared[slot];
154+
if (existing != null) {
155+
if (existing.className.contentEquals(className)) {
156+
// apply filter to class-loader key, -1 always matches
157+
if (existing.classLoaderKeyId < 0
158+
|| classLoaderKeyFilter.test(existing.classLoaderKeyId)) {
159+
// use global TICKS as a substitute for access time
160+
// TICKS is only incremented in 'share' for performance reasons
161+
existing.accessed = TICKS.get();
162+
return (T) existing.classInfo;
163+
}
164+
// fall-through and quit; name matched but class-loader didn't
165+
} else if (i < MAX_HASH_ATTEMPTS) {
166+
continue; // rehash and try again
167+
}
168+
}
169+
// quit search when:
170+
// * we find an empty slot (we know there won't be further info)
171+
// * we find a slot with the same name but different class-loader
172+
// * we've exhausted all hash attempts
173+
return null;
174+
}
175+
}
176+
135177
/**
136178
* Shares information for the given class-name, under the given class-loader key.
137179
*

utils/src/test/java/datadog/instrument/utils/ClassInfoCacheTest.java

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
import java.util.HashSet;
1010
import java.util.Set;
11+
import java.util.function.IntPredicate;
1112
import org.junit.jupiter.api.Test;
1213

1314
class ClassInfoCacheTest {
@@ -19,6 +20,11 @@ void basicOperation() {
1920
ClassLoader myCL = newCL();
2021
ClassLoader notMyCL = newCL();
2122

23+
int myCLKey = ClassLoaderIndex.getClassLoaderKeyId(myCL);
24+
25+
IntPredicate myCLFilter = sameCLKey(myCLKey);
26+
IntPredicate notMyCLFilter = myCLFilter.negate();
27+
2228
assertNull(cache.find("example.test.MyGlobalClass"));
2329
assertNull(cache.find("example.test.MyLocalClass"));
2430
assertNull(cache.find("example.test.NotMyClass"));
@@ -31,6 +37,14 @@ void basicOperation() {
3137
assertNull(cache.find("example.test.MyLocalClass", notMyCL));
3238
assertNull(cache.find("example.test.NotMyClass", notMyCL));
3339

40+
assertNull(cache.find("example.test.MyGlobalClass", myCLFilter));
41+
assertNull(cache.find("example.test.MyLocalClass", myCLFilter));
42+
assertNull(cache.find("example.test.NotMyClass", myCLFilter));
43+
44+
assertNull(cache.find("example.test.MyGlobalClass", notMyCLFilter));
45+
assertNull(cache.find("example.test.MyLocalClass", notMyCLFilter));
46+
assertNull(cache.find("example.test.NotMyClass", notMyCLFilter));
47+
3448
cache.share("example.test.MyGlobalClass", "my global data");
3549
cache.share("example.test.MyLocalClass", "my local data", myCL);
3650

@@ -46,6 +60,14 @@ void basicOperation() {
4660
assertNull(cache.find("example.test.MyLocalClass", notMyCL));
4761
assertNull(cache.find("example.test.NotMyClass", notMyCL));
4862

63+
assertEquals("my global data", cache.find("example.test.MyGlobalClass", myCLFilter));
64+
assertEquals("my local data", cache.find("example.test.MyLocalClass", myCLFilter));
65+
assertNull(cache.find("example.test.NotMyClass", myCLFilter));
66+
67+
assertEquals("my global data", cache.find("example.test.MyGlobalClass", notMyCLFilter));
68+
assertNull(cache.find("example.test.MyLocalClass", notMyCLFilter));
69+
assertNull(cache.find("example.test.NotMyClass", notMyCLFilter));
70+
4971
cache.clear();
5072

5173
assertNull(cache.find("example.test.MyGlobalClass"));
@@ -59,6 +81,14 @@ void basicOperation() {
5981
assertNull(cache.find("example.test.MyGlobalClass", notMyCL));
6082
assertNull(cache.find("example.test.MyLocalClass", notMyCL));
6183
assertNull(cache.find("example.test.NotMyClass", notMyCL));
84+
85+
assertNull(cache.find("example.test.MyGlobalClass", myCLFilter));
86+
assertNull(cache.find("example.test.MyLocalClass", myCLFilter));
87+
assertNull(cache.find("example.test.NotMyClass", myCLFilter));
88+
89+
assertNull(cache.find("example.test.MyGlobalClass", notMyCLFilter));
90+
assertNull(cache.find("example.test.MyLocalClass", notMyCLFilter));
91+
assertNull(cache.find("example.test.NotMyClass", notMyCLFilter));
6292
}
6393

6494
@Test
@@ -111,12 +141,20 @@ void overflow() {
111141
for (int i = 0; i < 300; i++) {
112142
if (overwritten.contains(i)) {
113143
assertNull(cache.find("example.MyClass" + i), "rem " + i);
144+
assertNull(cache.find("example.MyClass" + i, i), "rem " + i);
145+
assertNull(cache.find("example.MyClass" + i, sameCLKey(i)), "rem " + i);
114146
} else {
115147
assertEquals(i, cache.find("example.MyClass" + i), "add " + i);
148+
assertEquals(i, cache.find("example.MyClass" + i, i), "add " + i);
149+
assertEquals(i, cache.find("example.MyClass" + i, sameCLKey(i)), "add " + i);
116150
}
117151
}
118152
}
119153

154+
private static IntPredicate sameCLKey(int clKey) {
155+
return k -> k == clKey;
156+
}
157+
120158
private static ClassLoader newCL() {
121159
return new ClassLoader() {};
122160
}

0 commit comments

Comments
 (0)