Skip to content

Commit 1f1c037

Browse files
committed
Add BundleWiring instrumentation to improve tracer behaviour on OSGi:
* Rework our class-file location strategy to make it easier to mark agent requests * Record the type of agent class-loader requests to help with OSGi instrumentation * Widen class/resource lookup on OSGi for agent requests (and support lightweight probing) * Enable checks for common OSGi issues in the OSGi smoke test
1 parent 35c28f5 commit 1f1c037

File tree

13 files changed

+502
-23
lines changed

13 files changed

+502
-23
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package datadog.trace.bootstrap;
2+
3+
/**
4+
* Helps distinguish agent class-loading requests from application requests.
5+
*
6+
* <p>Should be used in a short try..finally block around the class-loader call.
7+
*/
8+
public enum AgentClassLoading {
9+
/** Lightweight class-loader probing to select instrumentations. */
10+
PROBING_CLASSLOADER,
11+
/** Locating class resources for Byte-Buddy. */
12+
LOCATING_CLASS,
13+
/** Injecting helper classes into class-loaders. */
14+
INJECTING_HELPERS;
15+
16+
private static final ThreadLocal<AgentClassLoading> REQUEST = new ThreadLocal<>();
17+
18+
/**
19+
* Gets the current agent class-loading request type; {@code null} if there's no agent request.
20+
*/
21+
public static AgentClassLoading type() {
22+
return REQUEST.get();
23+
}
24+
25+
/** Records that this agent class-loading request has begun. */
26+
public final void begin() {
27+
REQUEST.set(this);
28+
}
29+
30+
/** Records that this agent class-loading request has ended. */
31+
public final void end() {
32+
REQUEST.remove();
33+
}
34+
}

dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/ClassLoaderMatcher.java

+11-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package datadog.trace.agent.tooling;
22

3+
import static datadog.trace.bootstrap.AgentClassLoading.PROBING_CLASSLOADER;
4+
35
import datadog.trace.api.Tracer;
46
import datadog.trace.bootstrap.PatchLogger;
57
import datadog.trace.bootstrap.WeakCache;
@@ -140,12 +142,17 @@ private WeakCache<ClassLoader, Boolean> getCache() {
140142
}
141143

142144
private boolean hasResources(final ClassLoader cl) {
143-
for (final String resource : resources) {
144-
if (cl.getResource(resource) == null) {
145-
return false;
145+
PROBING_CLASSLOADER.begin();
146+
try {
147+
for (final String resource : resources) {
148+
if (cl.getResource(resource) == null) {
149+
return false;
150+
}
146151
}
152+
return true;
153+
} finally {
154+
PROBING_CLASSLOADER.end();
147155
}
148-
return true;
149156
}
150157

151158
@Override

dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/HelperInjector.java

+9-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package datadog.trace.agent.tooling;
22

33
import static datadog.trace.agent.tooling.ClassLoaderMatcher.BOOTSTRAP_CLASSLOADER;
4+
import static datadog.trace.bootstrap.AgentClassLoading.INJECTING_HELPERS;
45

56
import datadog.trace.api.Config;
67
import java.io.File;
@@ -165,21 +166,28 @@ private Map<String, Class<?>> injectBootstrapClassLoader(
165166

166167
// Failures to create a tempDir are propagated as IOException and handled by transform
167168
final File tempDir = createTempDir();
169+
INJECTING_HELPERS.begin();
168170
try {
169171
return ClassInjector.UsingInstrumentation.of(
170172
tempDir,
171173
ClassInjector.UsingInstrumentation.Target.BOOTSTRAP,
172174
AgentInstaller.getInstrumentation())
173175
.injectRaw(classnameToBytes);
174176
} finally {
177+
INJECTING_HELPERS.end();
175178
// Delete fails silently
176179
deleteTempDir(tempDir);
177180
}
178181
}
179182

180183
private Map<String, Class<?>> injectClassLoader(
181184
final ClassLoader classLoader, final Map<String, byte[]> classnameToBytes) {
182-
return new ClassInjector.UsingReflection(classLoader).injectRaw(classnameToBytes);
185+
INJECTING_HELPERS.begin();
186+
try {
187+
return new ClassInjector.UsingReflection(classLoader).injectRaw(classnameToBytes);
188+
} finally {
189+
INJECTING_HELPERS.end();
190+
}
183191
}
184192

185193
private void ensureModuleCanReadHelperModules(final JavaModule target) {

dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/bytebuddy/DDCachingPoolStrategy.java

+4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package datadog.trace.agent.tooling.bytebuddy;
22

3+
import static datadog.trace.bootstrap.AgentClassLoading.LOCATING_CLASS;
34
import static net.bytebuddy.agent.builder.AgentBuilder.PoolStrategy;
45

56
import com.googlecode.concurrentlinkedhashmap.ConcurrentLinkedHashMap;
@@ -295,6 +296,7 @@ public TypeDescription resolve() {
295296
if (!isResolved) {
296297
Class<?> klass = null;
297298
ClassLoader classLoader = null;
299+
LOCATING_CLASS.begin();
298300
try {
299301
// Please note that by doing a loadClass, the type we are resolving will bypass
300302
// transformation since we are in the middle of a transformation. This should
@@ -310,6 +312,8 @@ public TypeDescription resolve() {
310312
klass = Class.forName(className, false, null);
311313
}
312314
} catch (Throwable ignored) {
315+
} finally {
316+
LOCATING_CLASS.end();
313317
}
314318
if (klass != null) {
315319
// We managed to load the class
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package datadog.trace.agent.tooling.bytebuddy;
2+
3+
import static datadog.trace.bootstrap.AgentClassLoading.LOCATING_CLASS;
4+
5+
import datadog.trace.agent.tooling.Utils;
6+
import java.io.IOException;
7+
import java.io.InputStream;
8+
import java.lang.ref.WeakReference;
9+
import net.bytebuddy.dynamic.ClassFileLocator;
10+
import net.bytebuddy.utility.StreamDrainer;
11+
12+
/**
13+
* Locate resources with the loading classloader. Because of a quirk with the way classes appended
14+
* to the bootstrap classpath work, we first check our bootstrap proxy. If the loading classloader
15+
* cannot find the desired resource, check up the classloader hierarchy until a resource is found.
16+
*/
17+
public final class DDClassFileLocator extends WeakReference<ClassLoader>
18+
implements ClassFileLocator {
19+
20+
public DDClassFileLocator(final ClassLoader classLoader) {
21+
super(classLoader);
22+
}
23+
24+
@Override
25+
public Resolution locate(final String className) throws IOException {
26+
String resourceName = className.replace('.', '/') + ".class";
27+
28+
// try bootstrap first
29+
Resolution resolution = loadClassResource(Utils.getBootstrapProxy(), resourceName);
30+
ClassLoader cl = get();
31+
32+
// now go up the classloader hierarchy
33+
if (null == resolution && null != cl) {
34+
LOCATING_CLASS.begin();
35+
try {
36+
do {
37+
resolution = loadClassResource(cl, resourceName);
38+
cl = cl.getParent();
39+
} while (null == resolution && null != cl);
40+
} finally {
41+
LOCATING_CLASS.end();
42+
}
43+
}
44+
45+
return resolution != null ? resolution : new Resolution.Illegal(className);
46+
}
47+
48+
@Override
49+
public void close() {
50+
// nothing to close
51+
}
52+
53+
private static Resolution loadClassResource(
54+
final ClassLoader classLoader, final String resourceName) throws IOException {
55+
try (InputStream in = classLoader.getResourceAsStream(resourceName)) {
56+
if (null != in) {
57+
return new Resolution.Explicit(StreamDrainer.DEFAULT.drain(in));
58+
}
59+
return null;
60+
}
61+
}
62+
}
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,18 @@
11
package datadog.trace.agent.tooling.bytebuddy;
22

3-
import datadog.trace.agent.tooling.Utils;
4-
import java.util.ArrayList;
5-
import java.util.List;
63
import net.bytebuddy.agent.builder.AgentBuilder;
74
import net.bytebuddy.dynamic.ClassFileLocator;
85
import net.bytebuddy.utility.JavaModule;
96

10-
/**
11-
* Locate resources with the loading classloader. Because of a quirk with the way classes appended
12-
* to the bootstrap classpath work, we first check our bootstrap proxy. If the loading classloader
13-
* cannot find the desired resource, check up the classloader hierarchy until a resource is found.
14-
*/
15-
public class DDLocationStrategy implements AgentBuilder.LocationStrategy {
7+
/** Strategy that uses {@link DDClassFileLocator} to locate class files. */
8+
public final class DDLocationStrategy implements AgentBuilder.LocationStrategy {
169
public ClassFileLocator classFileLocator(final ClassLoader classLoader) {
1710
return classFileLocator(classLoader, null);
1811
}
1912

2013
@Override
21-
public ClassFileLocator classFileLocator(ClassLoader classLoader, final JavaModule javaModule) {
22-
final List<ClassFileLocator> locators = new ArrayList<>();
23-
locators.add(ClassFileLocator.ForClassLoader.of(Utils.getBootstrapProxy()));
24-
while (classLoader != null) {
25-
locators.add(ClassFileLocator.ForClassLoader.WeaklyReferenced.of(classLoader));
26-
classLoader = classLoader.getParent();
27-
}
28-
return new ClassFileLocator.Compound(locators.toArray(new ClassFileLocator[0]));
14+
public ClassFileLocator classFileLocator(
15+
final ClassLoader classLoader, final JavaModule javaModule) {
16+
return new DDClassFileLocator(classLoader);
2917
}
3018
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
muzzle {
2+
// old coordinates
3+
pass {
4+
group = 'org.osgi'
5+
module = 'org.osgi.core'
6+
versions = '[4.3,]'
7+
}
8+
9+
// new coordinates
10+
pass {
11+
group = 'org.osgi'
12+
module = 'osgi.core'
13+
versions = '[4.3.1,]'
14+
}
15+
}
16+
17+
apply from: "$rootDir/gradle/java.gradle"
18+
19+
dependencies {
20+
compileOnly group: 'org.osgi', name: 'org.osgi.core', version: '4.3.0'
21+
}

0 commit comments

Comments
 (0)