opentelemetry-java-instrume.../javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/AgentStarterImpl.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;
}
}
}