Skip to content

Commit 48d3b08

Browse files
committed
Initialize java.net.InetAddress after VM and agent are initialized
1 parent a3b66bd commit 48d3b08

File tree

2 files changed

+147
-0
lines changed

2 files changed

+147
-0
lines changed

javaagent-bootstrap/src/main/java/io/opentelemetry/javaagent/bootstrap/AgentInitializer.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ public final class AgentInitializer {
2626
@Nullable private static ClassLoader agentClassLoader = null;
2727
@Nullable private static AgentStarter agentStarter = null;
2828
private static boolean isSecurityManagerSupportEnabled = false;
29+
private static boolean agentStarted = false;
2930

3031
public static void initialize(Instrumentation inst, File javaagentFile, boolean fromPremain)
3132
throws Exception {
@@ -51,6 +52,7 @@ public Void run() throws Exception {
5152
agentStarter = createAgentStarter(agentClassLoader, inst, javaagentFile);
5253
if (!fromPremain || !delayAgentStart()) {
5354
agentStarter.start();
55+
agentStarted = true;
5456
}
5557
return null;
5658
}
@@ -149,11 +151,17 @@ public static void delayedStartHook() throws Exception {
149151
@Override
150152
public Void run() {
151153
agentStarter.start();
154+
agentStarted = true;
152155
return null;
153156
}
154157
});
155158
}
156159

160+
@SuppressWarnings("unused")
161+
public static boolean isAgentStarted() {
162+
return agentStarted;
163+
}
164+
157165
public static ClassLoader getExtensionsClassLoader() {
158166
// agentStarter can be null when running tests
159167
return agentStarter != null ? agentStarter.getExtensionClassLoader() : null;

javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/AgentStarterImpl.java

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import org.objectweb.asm.ClassReader;
2121
import org.objectweb.asm.ClassVisitor;
2222
import org.objectweb.asm.ClassWriter;
23+
import org.objectweb.asm.Label;
2324
import org.objectweb.asm.MethodVisitor;
2425
import org.objectweb.asm.Opcodes;
2526
import org.objectweb.asm.Type;
@@ -68,6 +69,8 @@ public boolean delayStart() {
6869

6970
@Override
7071
public void start() {
72+
init();
73+
7174
EarlyInitAgentConfig earlyConfig = EarlyInitAgentConfig.create();
7275
extensionClassLoader = createExtensionClassLoader(getClass().getClassLoader(), earlyConfig);
7376

@@ -115,6 +118,29 @@ public void start() {
115118
}
116119
}
117120

121+
private void init() {
122+
instrumentInetAddress();
123+
}
124+
125+
private void instrumentInetAddress() {
126+
InetAddressClassFileTransformer transformer = new InetAddressClassFileTransformer();
127+
instrumentation.addTransformer(transformer, true);
128+
129+
try {
130+
Class<?> clazz = Class.forName("java.net.InetAddress", false, null);
131+
if (transformer.transformed) {
132+
// InetAddress was loaded and got transformed
133+
return;
134+
}
135+
// InetAddress was already loaded before we set up transformer
136+
instrumentation.retransformClasses(clazz);
137+
} catch (ClassNotFoundException | UnmodifiableClassException ignore) {
138+
// ignore
139+
} finally {
140+
instrumentation.removeTransformer(transformer);
141+
}
142+
}
143+
118144
@SuppressWarnings("SystemOut")
119145
private static void logUnrecognizedLoggerImplWarning(String loggerImplementationName) {
120146
System.err.println(
@@ -180,4 +206,117 @@ public void visitCode() {
180206
return hookInserted ? cw.toByteArray() : null;
181207
}
182208
}
209+
210+
private static class InetAddressClassFileTransformer implements ClassFileTransformer {
211+
boolean hookInserted = false;
212+
boolean transformed = false;
213+
boolean wrapperMethodCreated = false;
214+
215+
private static void createWrapperMethod(ClassWriter cw) {
216+
/*
217+
private static boolean isAgentAndVmBooted();
218+
Code:
219+
0: invokestatic #X // Method io/opentelemetry/javaagent/bootstrap/AgentInitializer/isAgentStarted:()Z
220+
3: ifeq 16
221+
6: invokestatic #Y // Method jdk/internal/misc/VM.isBooted:()Z
222+
9: ifeq 16
223+
12: iconst_1
224+
13: goto 17
225+
16: iconst_0
226+
17: ireturn
227+
*/
228+
229+
String descriptor = Type.getMethodDescriptor(Type.BOOLEAN_TYPE);
230+
Label elseLabel = new Label();
231+
Label endLabel = new Label();
232+
233+
MethodVisitor mv =
234+
cw.visitMethod(
235+
Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC,
236+
"isAgentAndVmBooted",
237+
descriptor,
238+
null,
239+
null);
240+
mv.visitCode();
241+
242+
mv.visitMethodInsn(
243+
Opcodes.INVOKESTATIC,
244+
Type.getInternalName(AgentInitializer.class),
245+
"isAgentStarted",
246+
descriptor,
247+
false);
248+
mv.visitJumpInsn(Opcodes.IFEQ, elseLabel);
249+
mv.visitMethodInsn(
250+
Opcodes.INVOKESTATIC, "jdk/internal/misc/VM", "isBooted", descriptor, false);
251+
mv.visitJumpInsn(Opcodes.IFEQ, elseLabel);
252+
mv.visitInsn(Opcodes.ICONST_1);
253+
mv.visitJumpInsn(Opcodes.GOTO, endLabel);
254+
mv.visitLabel(elseLabel);
255+
mv.visitInsn(Opcodes.ICONST_0);
256+
mv.visitLabel(endLabel);
257+
mv.visitInsn(Opcodes.IRETURN);
258+
259+
mv.visitMaxs(0, 0);
260+
mv.visitEnd();
261+
}
262+
263+
@Override
264+
public byte[] transform(
265+
ClassLoader loader,
266+
String className,
267+
Class<?> classBeingRedefined,
268+
ProtectionDomain protectionDomain,
269+
byte[] classfileBuffer) {
270+
if (!"java/net/InetAddress".equals(className)) {
271+
return null;
272+
}
273+
transformed = true;
274+
ClassReader cr = new ClassReader(classfileBuffer);
275+
ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS);
276+
ClassVisitor cv =
277+
new ClassVisitor(AsmApi.VERSION, cw) {
278+
@Override
279+
public MethodVisitor visitMethod(
280+
int access, String name, String descriptor, String signature, String[] exceptions) {
281+
MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);
282+
// We don't want to patch "jdk.internal.misc.VM.isBooted" call in our wrapper
283+
if ("isAgentAndVmBooted".equals(name)) {
284+
return mv;
285+
}
286+
return new MethodVisitor(api, mv) {
287+
@Override
288+
public void visitMethodInsn(
289+
int opcode,
290+
String ownerClassName,
291+
String methodName,
292+
String descriptor,
293+
boolean isInterface) {
294+
if ("jdk/internal/misc/VM".equals(ownerClassName)
295+
&& "isBooted".equals(methodName)) {
296+
// Create wrapper method only once
297+
if (!wrapperMethodCreated) {
298+
createWrapperMethod(cw);
299+
wrapperMethodCreated = true;
300+
}
301+
super.visitMethodInsn(
302+
Opcodes.INVOKESTATIC,
303+
"java/net/InetAddress",
304+
"isAgentAndVmBooted",
305+
"()Z",
306+
isInterface);
307+
hookInserted = true;
308+
} else {
309+
super.visitMethodInsn(
310+
opcode, ownerClassName, methodName, descriptor, isInterface);
311+
}
312+
}
313+
};
314+
}
315+
};
316+
317+
cr.accept(cv, 0);
318+
319+
return hookInserted ? cw.toByteArray() : null;
320+
}
321+
}
183322
}

0 commit comments

Comments
 (0)