Skip to content

Commit a611bf7

Browse files
committed
try anoter approach
1 parent c8fac05 commit a611bf7

File tree

8 files changed

+98
-228
lines changed

8 files changed

+98
-228
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
plugins {
2+
id("otel.java-conventions")
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.javaagent.instrumentation.internal.classloader.stub;
7+
8+
import java.security.ProtectionDomain;
9+
10+
/**
11+
* A placeholder for java.lang.ClassLoader to allow compilation of advice classes that invoke
12+
* protected methods of ClassLoader (like defineClass and findLoadedClass). During the build we'll
13+
* use shadow plugin to replace reference to this class with the real java.lang.ClassLoader.
14+
*/
15+
@SuppressWarnings("JavaLangClash")
16+
public abstract class ClassLoader {
17+
public abstract Class<?> findLoadedClass(String name);
18+
19+
public abstract Class<?> defineClass(
20+
String name, byte[] b, int off, int len, ProtectionDomain protectionDomain);
21+
}

instrumentation/internal/internal-class-loader/javaagent-integration-tests/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,5 @@ dependencies {
66
compileOnly("org.apache.commons:commons-lang3:3.12.0")
77
testImplementation("org.apache.commons:commons-lang3:3.12.0")
88

9-
testInstrumentation(project(":instrumentation:internal:internal-class-loader:javaagent"))
9+
testInstrumentation(project(":instrumentation:internal:internal-class-loader:javaagent", configuration = "shadow"))
1010
}

instrumentation/internal/internal-class-loader/javaagent/build.gradle.kts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ plugins {
55
dependencies {
66
compileOnly(project(":javaagent-bootstrap"))
77
compileOnly(project(":javaagent-tooling"))
8+
compileOnly(project(":instrumentation:internal:internal-class-loader:compile-stub"))
89

910
testImplementation(project(":javaagent-bootstrap"))
1011

@@ -21,3 +22,9 @@ dependencies {
2122
testImplementation("org.eclipse.platform:org.eclipse.osgi:3.13.200")
2223
testImplementation("org.apache.felix:org.apache.felix.framework:6.0.2")
2324
}
25+
26+
tasks {
27+
shadowJar {
28+
relocate("io.opentelemetry.javaagent.instrumentation.internal.classloader.stub", "java.lang")
29+
}
30+
}

instrumentation/internal/internal-class-loader/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/classloader/ClassLoaderInstrumentationModule.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,8 @@ public List<String> injectedClassNames() {
3535
@Override
3636
public List<TypeInstrumentation> typeInstrumentations() {
3737
return asList(
38-
// added first so it would get applied after other instrumentations
39-
new LoadInjectedClassInstrumentation(),
4038
new BootDelegationInstrumentation(),
39+
new LoadInjectedClassInstrumentation(),
4140
new ResourceInjectionInstrumentation(),
4241
new DefineClassInstrumentation());
4342
}

instrumentation/internal/internal-class-loader/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/classloader/LoadInjectedClassInstrumentation.java

Lines changed: 63 additions & 224 deletions
Original file line numberDiff line numberDiff line change
@@ -6,28 +6,24 @@
66
package io.opentelemetry.javaagent.instrumentation.internal.classloader;
77

88
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.extendsClass;
9+
import static io.opentelemetry.javaagent.instrumentation.internal.classloader.AdviceUtil.applyInlineAdvice;
10+
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
11+
import static net.bytebuddy.matcher.ElementMatchers.isProtected;
12+
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
13+
import static net.bytebuddy.matcher.ElementMatchers.isStatic;
914
import static net.bytebuddy.matcher.ElementMatchers.named;
15+
import static net.bytebuddy.matcher.ElementMatchers.not;
16+
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
17+
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
1018

1119
import io.opentelemetry.javaagent.bootstrap.InjectedClassHelper;
12-
import io.opentelemetry.javaagent.bootstrap.InjectedClassHelper.HelperClassInfo;
1320
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
1421
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
15-
import io.opentelemetry.javaagent.extension.instrumentation.internal.AsmApi;
16-
import net.bytebuddy.ClassFileVersion;
17-
import net.bytebuddy.asm.AsmVisitorWrapper;
18-
import net.bytebuddy.description.field.FieldDescription;
19-
import net.bytebuddy.description.field.FieldList;
20-
import net.bytebuddy.description.method.MethodList;
22+
import net.bytebuddy.asm.Advice;
23+
import net.bytebuddy.asm.Advice.AssignReturned;
24+
import net.bytebuddy.description.method.MethodDescription;
2125
import net.bytebuddy.description.type.TypeDescription;
22-
import net.bytebuddy.implementation.Implementation;
2326
import net.bytebuddy.matcher.ElementMatcher;
24-
import net.bytebuddy.pool.TypePool;
25-
import org.objectweb.asm.ClassVisitor;
26-
import org.objectweb.asm.ClassWriter;
27-
import org.objectweb.asm.Label;
28-
import org.objectweb.asm.MethodVisitor;
29-
import org.objectweb.asm.Opcodes;
30-
import org.objectweb.asm.Type;
3127

3228
/**
3329
* This instrumentation inserts loading of our injected helper classes at the start of {@code
@@ -42,220 +38,63 @@ public ElementMatcher<TypeDescription> typeMatcher() {
4238

4339
@Override
4440
public void transform(TypeTransformer transformer) {
45-
transformer.applyTransformer(
46-
(builder, typeDescription, classLoader, module, protectionDomain) ->
47-
builder.visit(
48-
new AsmVisitorWrapper() {
49-
@Override
50-
public int mergeWriter(int flags) {
51-
return flags | ClassWriter.COMPUTE_MAXS;
52-
}
53-
54-
@Override
55-
public int mergeReader(int flags) {
56-
return flags;
57-
}
58-
59-
@Override
60-
public ClassVisitor wrap(
61-
TypeDescription instrumentedType,
62-
ClassVisitor classVisitor,
63-
Implementation.Context implementationContext,
64-
TypePool typePool,
65-
FieldList<FieldDescription.InDefinedShape> fields,
66-
MethodList<?> methods,
67-
int writerFlags,
68-
int readerFlags) {
69-
return new ClassLoaderClassVisitor(classVisitor);
70-
}
71-
}));
41+
ElementMatcher.Junction<MethodDescription> methodMatcher =
42+
isMethod()
43+
.and(named("loadClass"))
44+
.and(
45+
takesArguments(1)
46+
.and(takesArgument(0, String.class))
47+
.or(
48+
takesArguments(2)
49+
.and(takesArgument(0, String.class))
50+
.and(takesArgument(1, boolean.class))))
51+
.and(isPublic().or(isProtected()))
52+
.and(not(isStatic()));
53+
// Inline instrumentation to prevent problems with invokedynamic-recursion
54+
applyInlineAdvice(transformer, methodMatcher, this.getClass().getName() + "$LoadClassAdvice");
7255
}
7356

74-
private static class ClassLoaderClassVisitor extends ClassVisitor implements Opcodes {
75-
private String internalClassName;
76-
private boolean frames;
77-
78-
ClassLoaderClassVisitor(ClassVisitor classVisitor) {
79-
super(AsmApi.VERSION, classVisitor);
80-
}
81-
82-
@Override
83-
public void visit(
84-
int version,
85-
int access,
86-
String name,
87-
String signature,
88-
String superName,
89-
String[] interfaces) {
90-
super.visit(version, access, name, signature, superName, interfaces);
91-
internalClassName = name;
92-
frames = ClassFileVersion.ofMinorMajor(version).isAtLeast(ClassFileVersion.JAVA_V6);
93-
}
94-
95-
@Override
96-
public MethodVisitor visitMethod(
97-
int access, String name, String descriptor, String signature, String[] exceptions) {
98-
MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);
99-
boolean loadClassMethod =
100-
"loadClass".equals(name)
101-
&& ("(Ljava/lang/String;)Ljava/lang/Class;".equals(descriptor)
102-
|| "(Ljava/lang/String;Z)Ljava/lang/Class;".equals(descriptor));
103-
if (!loadClassMethod) {
104-
return mv;
105-
}
106-
107-
int argumentCount = Type.getArgumentTypes(descriptor).length;
108-
return new MethodVisitor(api, mv) {
109-
@Override
110-
public void visitCode() {
111-
super.visitCode();
112-
113-
// inserts the following at the start of the loadClass method:
114-
/*
115-
InjectedClassHelper.HelperClassInfo helperClassInfo = InjectedClassHelper.getHelperClassInfo(this, name);
116-
if (helperClassInfo != null) {
117-
Class<?> clazz = findLoadedClass(name);
118-
if (clazz != null) {
119-
return clazz;
120-
}
121-
try {
122-
byte[] bytes = helperClassInfo.getClassBytes();
123-
return defineClass(name, bytes, 0, bytes.length, helperClassInfo.getProtectionDomain());
124-
} catch (LinkageError error) {
125-
clazz = findLoadedClass(name);
126-
if (clazz != null) {
127-
return clazz;
128-
}
129-
throw error;
130-
}
131-
}
132-
*/
133-
134-
Label startTry = new Label();
135-
Label endTry = new Label();
136-
Label handler = new Label();
137-
mv.visitTryCatchBlock(startTry, endTry, handler, "java/lang/LinkageError");
138-
// InjectedClassHelper.HelperClassInfo helperClassInfo = InjectedClassHelper
139-
// .getHelperClassInfo(this, name);
140-
mv.visitVarInsn(ALOAD, 0);
141-
mv.visitVarInsn(ALOAD, 1);
142-
mv.visitMethodInsn(
143-
INVOKESTATIC,
144-
Type.getInternalName(InjectedClassHelper.class),
145-
"getHelperClassInfo",
146-
"(Ljava/lang/ClassLoader;Ljava/lang/String;)"
147-
+ Type.getDescriptor(HelperClassInfo.class),
148-
false);
149-
mv.visitVarInsn(ASTORE, argumentCount + 1); // store helperClassInfo
150-
mv.visitVarInsn(ALOAD, argumentCount + 1);
151-
Label notHelperClass = new Label();
152-
mv.visitJumpInsn(IFNULL, notHelperClass);
153-
154-
// getHelperClassInfo returned non-null
155-
// Class<?> clazz = findLoadedClass(name);
156-
mv.visitVarInsn(ALOAD, 0);
157-
mv.visitVarInsn(ALOAD, 1);
158-
mv.visitMethodInsn(
159-
INVOKEVIRTUAL,
160-
internalClassName,
161-
"findLoadedClass",
162-
"(Ljava/lang/String;)Ljava/lang/Class;",
163-
false);
164-
mv.visitVarInsn(ASTORE, argumentCount + 2); // store clazz
165-
mv.visitVarInsn(ALOAD, argumentCount + 2);
166-
mv.visitJumpInsn(IFNULL, startTry);
167-
168-
// findLoadedClass returned non-null
169-
// return clazz
170-
mv.visitVarInsn(ALOAD, argumentCount + 2);
171-
mv.visitInsn(ARETURN);
172-
173-
mv.visitLabel(startTry);
174-
if (frames) {
175-
mv.visitFrame(
176-
Opcodes.F_APPEND,
177-
2,
178-
new Object[] {Type.getInternalName(HelperClassInfo.class), "java/lang/Class"},
179-
0,
180-
null);
181-
}
182-
// byte[] bytes = helperClassInfo.getClassBytes();
183-
mv.visitVarInsn(ALOAD, argumentCount + 1);
184-
mv.visitMethodInsn(
185-
INVOKEINTERFACE,
186-
Type.getInternalName(HelperClassInfo.class),
187-
"getClassBytes",
188-
"()[B",
189-
true);
190-
mv.visitVarInsn(ASTORE, argumentCount + 3); // store bytes
191-
192-
// return defineClass(name, bytes, 0, bytes.length,
193-
// helperClassInfo.getProtectionDomain());
194-
mv.visitVarInsn(ALOAD, 0);
195-
mv.visitVarInsn(ALOAD, 1);
196-
mv.visitVarInsn(ALOAD, argumentCount + 3);
197-
mv.visitInsn(ICONST_0);
198-
mv.visitVarInsn(ALOAD, argumentCount + 3);
199-
mv.visitInsn(ARRAYLENGTH);
200-
mv.visitVarInsn(ALOAD, argumentCount + 1);
201-
mv.visitMethodInsn(
202-
INVOKEINTERFACE,
203-
Type.getInternalName(HelperClassInfo.class),
204-
"getProtectionDomain",
205-
"()Ljava/security/ProtectionDomain;",
206-
true);
207-
mv.visitMethodInsn(
208-
INVOKEVIRTUAL,
209-
internalClassName,
210-
"defineClass",
211-
"(Ljava/lang/String;[BIILjava/security/ProtectionDomain;)Ljava/lang/Class;",
212-
false);
213-
mv.visitLabel(endTry);
214-
mv.visitInsn(ARETURN);
215-
216-
mv.visitLabel(handler);
217-
if (frames) {
218-
mv.visitFrame(Opcodes.F_SAME1, 0, null, 1, new Object[] {"java/lang/LinkageError"});
219-
}
220-
mv.visitVarInsn(ASTORE, argumentCount + 3); // store LinkageError
221-
// clazz = findLoadedClass(name);
222-
mv.visitVarInsn(ALOAD, 0);
223-
mv.visitVarInsn(ALOAD, 1);
224-
mv.visitMethodInsn(
225-
INVOKEVIRTUAL,
226-
internalClassName,
227-
"findLoadedClass",
228-
"(Ljava/lang/String;)Ljava/lang/Class;",
229-
false);
230-
mv.visitVarInsn(ASTORE, argumentCount + 2); // score clazz
231-
mv.visitVarInsn(ALOAD, argumentCount + 2);
232-
Label throwError = new Label();
233-
mv.visitJumpInsn(IFNULL, throwError);
234-
// return clazz
235-
mv.visitVarInsn(ALOAD, argumentCount + 2);
236-
mv.visitInsn(ARETURN);
237-
mv.visitLabel(throwError);
238-
if (frames) {
239-
mv.visitFrame(Opcodes.F_APPEND, 1, new Object[] {"java/lang/LinkageError"}, 0, null);
240-
}
241-
// throw error
242-
mv.visitVarInsn(ALOAD, argumentCount + 3);
243-
mv.visitInsn(ATHROW);
244-
245-
mv.visitLabel(notHelperClass);
246-
if (frames) {
247-
mv.visitFrame(Opcodes.F_CHOP, 3, null, 0, null);
248-
// ensure there aren't two frames at the same location
249-
mv.visitInsn(NOP);
57+
@SuppressWarnings("unused")
58+
public static class LoadClassAdvice {
59+
60+
// Class loader stub is shaded back to the real class loader class. It is used to call protected
61+
// method from the advice that the complier won't let us call directly. During runtime it is
62+
// fine since this code is inlined into subclasses of ClassLoader that can call protected
63+
// methods.
64+
@Advice.OnMethodEnter(skipOn = Advice.OnNonDefaultValue.class)
65+
public static Class<?> onEnter(
66+
@Advice.This java.lang.ClassLoader classLoader,
67+
@Advice.This
68+
io.opentelemetry.javaagent.instrumentation.internal.classloader.stub.ClassLoader
69+
classLoaderStub,
70+
@Advice.Argument(0) String name) {
71+
InjectedClassHelper.HelperClassInfo helperClassInfo =
72+
InjectedClassHelper.getHelperClassInfo(classLoader, name);
73+
if (helperClassInfo != null) {
74+
Class<?> clazz = classLoaderStub.findLoadedClass(name);
75+
if (clazz != null) {
76+
return clazz;
77+
}
78+
try {
79+
byte[] bytes = helperClassInfo.getClassBytes();
80+
return classLoaderStub.defineClass(
81+
name, bytes, 0, bytes.length, helperClassInfo.getProtectionDomain());
82+
} catch (LinkageError error) {
83+
clazz = classLoaderStub.findLoadedClass(name);
84+
if (clazz != null) {
85+
return clazz;
25086
}
87+
throw error;
25188
}
89+
}
90+
return null;
91+
}
25292

253-
@Override
254-
public void visitMaxs(int maxStack, int maxLocals) {
255-
// minimally we have argumentCount parameters + this + 3 locals added by us
256-
super.visitMaxs(maxStack, Math.max(maxLocals, argumentCount + 1 + 3));
257-
}
258-
};
93+
@AssignReturned.ToReturned
94+
@Advice.OnMethodExit(onThrowable = Throwable.class)
95+
public static Class<?> onExit(
96+
@Advice.Return Class<?> originalResult, @Advice.Enter Class<?> loadedClass) {
97+
return loadedClass != null ? loadedClass : originalResult;
25998
}
26099
}
261100
}

javaagent/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ dependencies {
9494
baseJavaagentLibs(project(":instrumentation:opentelemetry-instrumentation-annotations-1.16:javaagent"))
9595
baseJavaagentLibs(project(":instrumentation:executors:javaagent"))
9696
baseJavaagentLibs(project(":instrumentation:internal:internal-application-logger:javaagent"))
97-
baseJavaagentLibs(project(":instrumentation:internal:internal-class-loader:javaagent"))
97+
baseJavaagentLibs(project(":instrumentation:internal:internal-class-loader:javaagent", configuration = "shadow"))
9898
baseJavaagentLibs(project(":instrumentation:internal:internal-eclipse-osgi-3.6:javaagent"))
9999
baseJavaagentLibs(project(":instrumentation:internal:internal-lambda:javaagent"))
100100
baseJavaagentLibs(project(":instrumentation:internal:internal-reflection:javaagent"))

settings.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,7 @@ include(":instrumentation:hystrix-1.4:javaagent")
264264
include(":instrumentation:influxdb-2.4:javaagent")
265265
include(":instrumentation:internal:internal-application-logger:bootstrap")
266266
include(":instrumentation:internal:internal-application-logger:javaagent")
267+
include(":instrumentation:internal:internal-class-loader:compile-stub")
267268
include(":instrumentation:internal:internal-class-loader:javaagent")
268269
include(":instrumentation:internal:internal-class-loader:javaagent-integration-tests")
269270
include(":instrumentation:internal:internal-eclipse-osgi-3.6:javaagent")

0 commit comments

Comments
 (0)