Skip to content

Commit 7e29c8f

Browse files
authored
Verify that there are no VirtualField.find calls in indy ready advice (open-telemetry#13689)
1 parent 104003a commit 7e29c8f

File tree

3 files changed

+104
-15
lines changed

3 files changed

+104
-15
lines changed

javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/instrumentation/indy/AdviceTransformer.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ class AdviceTransformer {
3737
private static final Type OBJECT_TYPE = Type.getType(Object.class);
3838
private static final Type OBJECT_ARRAY_TYPE = Type.getType(Object[].class);
3939

40+
static final Type ADVICE_ON_METHOD_ENTER = Type.getType(Advice.OnMethodEnter.class);
41+
static final Type ADVICE_ON_METHOD_EXIT = Type.getType(Advice.OnMethodExit.class);
42+
4043
static byte[] transform(byte[] bytes) {
4144
ClassReader cr = new ClassReader(bytes);
4245
ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS);
@@ -236,7 +239,6 @@ private static List<AdviceLocal> getLocals(MethodNode source) {
236239
return result;
237240
}
238241

239-
static final Type ADVICE_ON_METHOD_ENTER = Type.getType(Advice.OnMethodEnter.class);
240242
private static final Type ADVICE_ASSIGN_RETURNED_TO_RETURNED =
241243
Type.getType(Advice.AssignReturned.ToReturned.class);
242244
private static final Type ADVICE_ASSIGN_RETURNED_TO_ARGUMENTS =
@@ -266,8 +268,6 @@ private static boolean isEnterAdvice(MethodNode source) {
266268
return hasAnnotation(source, ADVICE_ON_METHOD_ENTER);
267269
}
268270

269-
static final Type ADVICE_ON_METHOD_EXIT = Type.getType(Advice.OnMethodExit.class);
270-
271271
private static boolean isExitAdvice(MethodNode source) {
272272
return hasAnnotation(source, ADVICE_ON_METHOD_EXIT);
273273
}

javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/instrumentation/indy/IndyTypeTransformerImpl.java

Lines changed: 36 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import io.opentelemetry.javaagent.tooling.bytebuddy.ExceptionHandlers;
1212
import java.io.FileOutputStream;
1313
import java.io.IOException;
14+
import java.util.function.BiFunction;
1415
import net.bytebuddy.agent.builder.AgentBuilder;
1516
import net.bytebuddy.asm.Advice;
1617
import net.bytebuddy.description.method.MethodDescription;
@@ -65,7 +66,32 @@ public void applyAdviceToMethod(
6566

6667
private ClassFileLocator getAdviceLocator(ClassLoader classLoader) {
6768
ClassFileLocator classFileLocator = ClassFileLocator.ForClassLoader.of(classLoader);
68-
return transformAdvice ? new AdviceLocator(classFileLocator) : classFileLocator;
69+
return new AdviceLocator(
70+
classFileLocator,
71+
(slashClassName, bytes) -> {
72+
if (transformAdvice) {
73+
// rewrite inline advice to a from accepted by indy instrumentation
74+
return transformAdvice(slashClassName, bytes);
75+
} else {
76+
// verify that advice does not call VirtualField.find
77+
// NOTE: this check is here to help converting the advice for the indy instrumentation,
78+
// it can be removed once the conversion is completed
79+
VirtualFieldChecker.check(bytes);
80+
return bytes;
81+
}
82+
});
83+
}
84+
85+
private static byte[] transformAdvice(String slashClassName, byte[] bytes) {
86+
byte[] result = AdviceTransformer.transform(bytes);
87+
if (result != null) {
88+
dump(slashClassName, result);
89+
InstrumentationModuleClassLoader.bytecodeOverride.put(
90+
slashClassName.replace('/', '.'), result);
91+
} else {
92+
result = bytes;
93+
}
94+
return result;
6995
}
7096

7197
@Override
@@ -79,15 +105,17 @@ public AgentBuilder.Identified.Extendable getAgentBuilder() {
79105

80106
private static class AdviceLocator implements ClassFileLocator {
81107
private final ClassFileLocator delegate;
108+
private final BiFunction<String, byte[], byte[]> transform;
82109

83-
AdviceLocator(ClassFileLocator delegate) {
110+
AdviceLocator(ClassFileLocator delegate, BiFunction<String, byte[], byte[]> transform) {
84111
this.delegate = delegate;
112+
this.transform = transform;
85113
}
86114

87115
@Override
88116
public Resolution locate(String name) throws IOException {
89117
Resolution resolution = delegate.locate(name);
90-
return new AdviceTransformingResolution(name, resolution);
118+
return new AdviceTransformingResolution(name, resolution, transform);
91119
}
92120

93121
@Override
@@ -99,10 +127,13 @@ public void close() throws IOException {
99127
private static class AdviceTransformingResolution implements Resolution {
100128
private final String name;
101129
private final Resolution delegate;
130+
private final BiFunction<String, byte[], byte[]> transform;
102131

103-
AdviceTransformingResolution(String name, Resolution delegate) {
132+
AdviceTransformingResolution(
133+
String name, Resolution delegate, BiFunction<String, byte[], byte[]> transform) {
104134
this.name = name;
105135
this.delegate = delegate;
136+
this.transform = transform;
106137
}
107138

108139
@Override
@@ -113,14 +144,7 @@ public boolean isResolved() {
113144
@Override
114145
public byte[] resolve() {
115146
byte[] bytes = delegate.resolve();
116-
byte[] result = AdviceTransformer.transform(bytes);
117-
if (result != null) {
118-
dump(name, result);
119-
InstrumentationModuleClassLoader.bytecodeOverride.put(name.replace('/', '.'), result);
120-
} else {
121-
result = bytes;
122-
}
123-
return result;
147+
return transform.apply(name, bytes);
124148
}
125149
}
126150

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.javaagent.tooling.instrumentation.indy;
7+
8+
import static io.opentelemetry.javaagent.tooling.instrumentation.indy.AdviceTransformer.ADVICE_ON_METHOD_ENTER;
9+
import static io.opentelemetry.javaagent.tooling.instrumentation.indy.AdviceTransformer.ADVICE_ON_METHOD_EXIT;
10+
import static io.opentelemetry.javaagent.tooling.instrumentation.indy.AdviceTransformer.hasAnnotation;
11+
12+
import io.opentelemetry.instrumentation.api.util.VirtualField;
13+
import io.opentelemetry.javaagent.extension.instrumentation.internal.AsmApi;
14+
import org.objectweb.asm.ClassReader;
15+
import org.objectweb.asm.MethodVisitor;
16+
import org.objectweb.asm.Opcodes;
17+
import org.objectweb.asm.Type;
18+
import org.objectweb.asm.tree.ClassNode;
19+
import org.objectweb.asm.tree.MethodNode;
20+
21+
/**
22+
* Check that advice does not call {@link VirtualField#find(Class, Class)}. For inline advice {@link
23+
* VirtualField#find(Class, Class)} calls in advice are rewritten for efficiency. In non-inline
24+
* advice we don't do such rewriting, we expect users to keep the result of {@link
25+
* VirtualField#find(Class, Class)} in a static field.
26+
*/
27+
class VirtualFieldChecker {
28+
29+
private static final Type VIRTUAL_FIELD_TYPE = Type.getType(VirtualField.class);
30+
31+
static void check(byte[] bytes) {
32+
ClassReader cr = new ClassReader(bytes);
33+
ClassNode classNode = new ClassNode();
34+
cr.accept(classNode, ClassReader.SKIP_FRAMES | ClassReader.SKIP_DEBUG);
35+
36+
String dotClassName = Type.getObjectType(classNode.name).getClassName();
37+
classNode.methods.forEach(m -> checkMethod(m, dotClassName));
38+
}
39+
40+
private static void checkMethod(MethodNode methodNode, String dotClassName) {
41+
if (!hasAnnotation(methodNode, ADVICE_ON_METHOD_ENTER)
42+
&& !hasAnnotation(methodNode, ADVICE_ON_METHOD_EXIT)) {
43+
return;
44+
}
45+
46+
methodNode.accept(
47+
new MethodVisitor(AsmApi.VERSION, null) {
48+
@Override
49+
public void visitMethodInsn(
50+
int opcode, String owner, String name, String descriptor, boolean isInterface) {
51+
if (opcode == Opcodes.INVOKESTATIC
52+
&& VIRTUAL_FIELD_TYPE.getInternalName().equals(owner)
53+
&& "find".equals(name)) {
54+
throw new IllegalStateException(
55+
"Found usage of VirtualField.find in advice "
56+
+ dotClassName
57+
+ "."
58+
+ methodNode.name);
59+
}
60+
}
61+
});
62+
}
63+
64+
private VirtualFieldChecker() {}
65+
}

0 commit comments

Comments
 (0)