Skip to content

Commit fbf0076

Browse files
authored
Use VirtualField for associating netty listener with wrapper (#5282)
* Use VirtualField for associating netty listener with wrapper * Move ignoring lambas for injected classes to LambdaTransformer
1 parent 666c22e commit fbf0076

File tree

4 files changed

+57
-13
lines changed

4 files changed

+57
-13
lines changed

instrumentation/internal/internal-lambda/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/lambda/LambdaTransformer.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
package io.opentelemetry.javaagent.instrumentation.internal.lambda;
77

88
import io.opentelemetry.javaagent.bootstrap.ClassFileTransformerHolder;
9+
import io.opentelemetry.javaagent.bootstrap.InjectedHelperClassDetector;
910
import java.lang.instrument.ClassFileTransformer;
1011

1112
/** Helper class for transforming lambda class bytes. */
@@ -29,6 +30,11 @@ private static boolean isJava9() {
2930
*/
3031
@SuppressWarnings("unused")
3132
public static byte[] transform(byte[] classBytes, String slashClassName, Class<?> targetClass) {
33+
// Skip transforming lambdas of injected helper classes.
34+
if (InjectedHelperClassDetector.isHelperClass(targetClass)) {
35+
return classBytes;
36+
}
37+
3238
ClassFileTransformer transformer = ClassFileTransformerHolder.getClassFileTransformer();
3339
if (transformer != null) {
3440
try {

instrumentation/netty/netty-4-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/netty/common/FutureListenerWrappers.java

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,25 +11,25 @@
1111
import io.netty.util.concurrent.ProgressiveFuture;
1212
import io.opentelemetry.context.Context;
1313
import io.opentelemetry.context.Scope;
14-
import io.opentelemetry.instrumentation.api.cache.Cache;
14+
import io.opentelemetry.instrumentation.api.field.VirtualField;
1515
import java.lang.ref.WeakReference;
1616

1717
public final class FutureListenerWrappers {
1818
// note: it's ok if the value is collected prior to the key, since this cache is only used to
1919
// remove the wrapped listener from the netty future, and if the value is collected prior to the
2020
// key, that means it's no longer used (referenced) by the netty future anyways.
2121
//
22-
// also note: this is not using VirtualField in case this is ever converted to library
23-
// instrumentation, because while the library implementation of VirtualField maintains a weak
24-
// reference to its keys, it maintains a strong reference to its values, and in this particular
25-
// case the wrapper listener (value) has a strong reference to original listener (key), which will
26-
// create a memory leak. which is not a problem in the javaagent's implementation of VirtualField,
27-
// since it injects the value directly into the key as a field, and so the value is only retained
28-
// strongly by the key, and so they can be collected together.
29-
private static final Cache<
22+
// also note: this is using WeakReference as VirtualField value in case this is ever converted to
23+
// library instrumentation, because while the library implementation of VirtualField maintains a
24+
// weak reference to its keys, it maintains a strong reference to its values, and in this
25+
// particular case the wrapper listener (value) has a strong reference to original listener (key),
26+
// which will create a memory leak. which is not a problem in the javaagent's implementation of
27+
// VirtualField, since it injects the value directly into the key as a field, and so the value is
28+
// only retained strongly by the key, and so they can be collected together.
29+
private static final VirtualField<
3030
GenericFutureListener<? extends Future<?>>,
3131
WeakReference<GenericFutureListener<? extends Future<?>>>>
32-
wrappers = Cache.weak();
32+
wrapperVirtualField = VirtualField.find(GenericFutureListener.class, WeakReference.class);
3333

3434
private static final ClassValue<Boolean> shouldWrap =
3535
new ClassValue<Boolean>() {
@@ -54,7 +54,7 @@ public static GenericFutureListener<?> wrap(
5454
// collected before we have a chance to make (and return) a strong reference to the wrapper
5555

5656
WeakReference<GenericFutureListener<? extends Future<?>>> resultReference =
57-
wrappers.get(delegate);
57+
wrapperVirtualField.get(delegate);
5858

5959
if (resultReference != null) {
6060
GenericFutureListener<? extends Future<?>> wrapper = resultReference.get();
@@ -75,14 +75,14 @@ public static GenericFutureListener<?> wrap(
7575
} else {
7676
wrapper = new WrappedFutureListener(context, (GenericFutureListener<Future<?>>) delegate);
7777
}
78-
wrappers.put(delegate, new WeakReference<>(wrapper));
78+
wrapperVirtualField.set(delegate, new WeakReference<>(wrapper));
7979
return wrapper;
8080
}
8181

8282
public static GenericFutureListener<? extends Future<?>> getWrapper(
8383
GenericFutureListener<? extends Future<?>> delegate) {
8484
WeakReference<GenericFutureListener<? extends Future<?>>> wrapperReference =
85-
wrappers.get(delegate);
85+
wrapperVirtualField.get(delegate);
8686
if (wrapperReference == null) {
8787
return delegate;
8888
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.javaagent.bootstrap;
7+
8+
import java.util.function.Function;
9+
10+
/** Helper class for detecting whether given class is an injected helper class. */
11+
public final class InjectedHelperClassDetector {
12+
13+
private InjectedHelperClassDetector() {}
14+
15+
private static volatile Function<Class<?>, Boolean> helperClassDetector;
16+
17+
/** Sets the {@link Function} for detecting injected helper classes. */
18+
public static void internalSetHelperClassDetector(
19+
Function<Class<?>, Boolean> helperClassDetector) {
20+
if (InjectedHelperClassDetector.helperClassDetector != null) {
21+
// Only possible by misuse of this API, just ignore.
22+
return;
23+
}
24+
InjectedHelperClassDetector.helperClassDetector = helperClassDetector;
25+
}
26+
27+
public static boolean isHelperClass(Class<?> clazz) {
28+
if (helperClassDetector == null) {
29+
return false;
30+
}
31+
return helperClassDetector.apply(clazz);
32+
}
33+
}

muzzle/src/main/java/io/opentelemetry/javaagent/tooling/HelperInjector.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import io.opentelemetry.instrumentation.api.cache.Cache;
99
import io.opentelemetry.javaagent.bootstrap.DefineClassContext;
1010
import io.opentelemetry.javaagent.bootstrap.HelperResources;
11+
import io.opentelemetry.javaagent.bootstrap.InjectedHelperClassDetector;
1112
import io.opentelemetry.javaagent.tooling.muzzle.HelperResource;
1213
import java.io.File;
1314
import java.io.IOException;
@@ -50,6 +51,10 @@ public class HelperInjector implements Transformer {
5051
private static final TransformSafeLogger logger =
5152
TransformSafeLogger.getLogger(HelperInjector.class);
5253

54+
static {
55+
InjectedHelperClassDetector.internalSetHelperClassDetector(HelperInjector::isInjectedClass);
56+
}
57+
5358
// Need this because we can't put null into the injectedClassLoaders map.
5459
private static final ClassLoader BOOTSTRAP_CLASSLOADER_PLACEHOLDER =
5560
new SecureClassLoader(null) {

0 commit comments

Comments
 (0)