128 lines
4.3 KiB
Java
128 lines
4.3 KiB
Java
/*
|
|
* Copyright The OpenTelemetry Authors
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
package io.opentelemetry.javaagent.tooling;
|
|
|
|
import io.opentelemetry.javaagent.bootstrap.AgentInitializer;
|
|
import io.opentelemetry.javaagent.bootstrap.AgentStarter;
|
|
import java.io.File;
|
|
import java.lang.instrument.ClassFileTransformer;
|
|
import java.lang.instrument.Instrumentation;
|
|
import java.lang.instrument.UnmodifiableClassException;
|
|
import java.security.ProtectionDomain;
|
|
import org.objectweb.asm.ClassReader;
|
|
import org.objectweb.asm.ClassVisitor;
|
|
import org.objectweb.asm.ClassWriter;
|
|
import org.objectweb.asm.MethodVisitor;
|
|
import org.objectweb.asm.Opcodes;
|
|
import org.objectweb.asm.Type;
|
|
|
|
/**
|
|
* Main entry point into code that is running inside agent class loader, used reflectively from
|
|
* {@code io.opentelemetry.javaagent.bootstrap.AgentInitializer}.
|
|
*/
|
|
public class AgentStarterImpl implements AgentStarter {
|
|
private final Instrumentation instrumentation;
|
|
private final File javaagentFile;
|
|
private ClassLoader extensionClassLoader;
|
|
|
|
public AgentStarterImpl(Instrumentation instrumentation, File javaagentFile) {
|
|
this.instrumentation = instrumentation;
|
|
this.javaagentFile = javaagentFile;
|
|
}
|
|
|
|
@Override
|
|
public boolean delayStart() {
|
|
LaunchHelperClassFileTransformer transformer = new LaunchHelperClassFileTransformer();
|
|
instrumentation.addTransformer(transformer, true);
|
|
|
|
try {
|
|
Class<?> clazz = Class.forName("sun.launcher.LauncherHelper", false, null);
|
|
if (transformer.transformed) {
|
|
// LauncherHelper was loaded and got transformed
|
|
return transformer.hookInserted;
|
|
}
|
|
// LauncherHelper was already loaded before we set up transformer
|
|
instrumentation.retransformClasses(clazz);
|
|
return transformer.hookInserted;
|
|
} catch (ClassNotFoundException | UnmodifiableClassException ignore) {
|
|
// ignore
|
|
} finally {
|
|
instrumentation.removeTransformer(transformer);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public void start() {
|
|
extensionClassLoader = createExtensionClassLoader(getClass().getClassLoader(), javaagentFile);
|
|
ClassLoader savedContextClassLoader = Thread.currentThread().getContextClassLoader();
|
|
try {
|
|
Thread.currentThread().setContextClassLoader(extensionClassLoader);
|
|
AgentInstaller.installBytebuddyAgent(instrumentation);
|
|
} finally {
|
|
Thread.currentThread().setContextClassLoader(savedContextClassLoader);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public ClassLoader getExtensionClassLoader() {
|
|
return extensionClassLoader;
|
|
}
|
|
|
|
private static ClassLoader createExtensionClassLoader(
|
|
ClassLoader agentClassLoader, File javaagentFile) {
|
|
return ExtensionClassLoader.getInstance(agentClassLoader, javaagentFile);
|
|
}
|
|
|
|
private static class LaunchHelperClassFileTransformer implements ClassFileTransformer {
|
|
boolean hookInserted = false;
|
|
boolean transformed = false;
|
|
|
|
@Override
|
|
public byte[] transform(
|
|
ClassLoader loader,
|
|
String className,
|
|
Class<?> classBeingRedefined,
|
|
ProtectionDomain protectionDomain,
|
|
byte[] classfileBuffer) {
|
|
if (!"sun/launcher/LauncherHelper".equals(className)) {
|
|
return null;
|
|
}
|
|
transformed = true;
|
|
ClassReader cr = new ClassReader(classfileBuffer);
|
|
ClassWriter cw = new ClassWriter(cr, 0);
|
|
ClassVisitor cv =
|
|
new ClassVisitor(Opcodes.ASM7, cw) {
|
|
@Override
|
|
public MethodVisitor visitMethod(
|
|
int access, String name, String descriptor, String signature, String[] exceptions) {
|
|
MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);
|
|
if ("checkAndLoadMain".equals(name)) {
|
|
return new MethodVisitor(api, mv) {
|
|
@Override
|
|
public void visitCode() {
|
|
super.visitCode();
|
|
hookInserted = true;
|
|
mv.visitMethodInsn(
|
|
Opcodes.INVOKESTATIC,
|
|
Type.getInternalName(AgentInitializer.class),
|
|
"delayedStartHook",
|
|
"()V",
|
|
false);
|
|
}
|
|
};
|
|
}
|
|
return mv;
|
|
}
|
|
};
|
|
cr.accept(cv, 0);
|
|
|
|
return hookInserted ? cw.toByteArray() : null;
|
|
}
|
|
}
|
|
}
|