/* * Copyright (c) 2006 Rogério Liesenfeld * This file is subject to the terms of the MIT license (see LICENSE.txt). */ package mockit.internal.startup; import java.io.*; import java.lang.management.*; import java.lang.reflect.*; import java.util.*; import javax.annotation.*; import static mockit.internal.util.Utilities.*; import com.sun.tools.attach.*; import com.sun.tools.attach.spi.*; import sun.tools.attach.*; public final class AgentLoader { private static final AttachProvider ATTACH_PROVIDER = new AttachProvider() { @Override @Nullable public String name() { return null; } @Override @Nullable public String type() { return null; } @Override @Nullable public VirtualMachine attachVirtualMachine(String id) { return null; } @Override @Nullable public List<VirtualMachineDescriptor> listVirtualMachines() { return null; } }; @Nonnull private final String jarFilePath; @Nullable private String pidForTargetVM; AgentLoader() { if (JAVA_VERSION < 1.6F) { throw new IllegalStateException("JMockit requires a Java 6+ VM"); } jarFilePath = PathToAgentJar.getPathToJarFile(); } public AgentLoader(@Nonnull String pid) { this(); pidForTargetVM = pid; } public void loadAgent(@Nullable String options) { VirtualMachine vm; if (AttachProvider.providers().isEmpty()) { if (HOTSPOT_VM) { vm = getVirtualMachineImplementationFromEmbeddedOnes(); } else { String helpMessage = getHelpMessageForNonHotSpotVM(); throw new IllegalStateException(helpMessage); } } else { vm = attachToRunningVM(); } loadAgentAndDetachFromRunningVM(vm, options); } @Nonnull private VirtualMachine getVirtualMachineImplementationFromEmbeddedOnes() { Class<? extends VirtualMachine> vmClass = findVirtualMachineClassAccordingToOS(); Class<?>[] parameterTypes = {AttachProvider.class, String.class}; String pid = getProcessIdForTargetVM(); try { // This is only done with Reflection to avoid the JVM pre-loading all the XyzVirtualMachine classes. Constructor<? extends VirtualMachine> vmConstructor = vmClass.getConstructor(parameterTypes); VirtualMachine newVM = vmConstructor.newInstance(ATTACH_PROVIDER, pid); return newVM; } catch (NoSuchMethodException e) { throw new RuntimeException(e); } catch (InvocationTargetException e) { throw new RuntimeException(e); } catch (InstantiationException e) { throw new RuntimeException(e); } catch (IllegalAccessException e) { throw new RuntimeException(e); } catch (NoClassDefFoundError e) { throw new IllegalStateException("Native library for Attach API not available in this JRE", e); } catch (UnsatisfiedLinkError e) { throw new IllegalStateException("Native library for Attach API not available in this JRE", e); } } @Nonnull private static Class<? extends VirtualMachine> findVirtualMachineClassAccordingToOS() { if (File.separatorChar == '\\') { return WindowsVirtualMachine.class; } String osName = System.getProperty("os.name"); if (osName.startsWith("Linux") || osName.startsWith("LINUX")) { return LinuxVirtualMachine.class; } if (osName.contains("FreeBSD") || osName.startsWith("Mac OS X")) { return BsdVirtualMachine.class; } if (osName.startsWith("Solaris") || osName.contains("SunOS")) { return SolarisVirtualMachine.class; } throw new IllegalStateException("Cannot use Attach API on unknown OS: " + osName); } @Nonnull private String getProcessIdForTargetVM() { if (pidForTargetVM != null) { return pidForTargetVM; } String nameOfRunningVM = ManagementFactory.getRuntimeMXBean().getName(); int p = nameOfRunningVM.indexOf('@'); return nameOfRunningVM.substring(0, p); } @Nonnull private String getHelpMessageForNonHotSpotVM() { String vmName = System.getProperty("java.vm.name"); String helpMessage = "To run on " + vmName; if (vmName.contains("J9")) { helpMessage += ", add <IBM SDK>/lib/tools.jar to the runtime classpath (before jmockit), or"; } return helpMessage + " use -javaagent:" + jarFilePath; } @Nonnull private VirtualMachine attachToRunningVM() { String pid = getProcessIdForTargetVM(); try { return VirtualMachine.attach(pid); } catch (AttachNotSupportedException e) { throw new RuntimeException(e); } catch (IOException e) { throw new RuntimeException(e); } } private void loadAgentAndDetachFromRunningVM(@Nonnull VirtualMachine vm, @Nullable String options) { try { vm.loadAgent(jarFilePath, options); vm.detach(); } catch (AgentLoadException e) { throw new IllegalStateException(e); } catch (AgentInitializationException e) { throw new IllegalStateException(e); } catch (IOException e) { throw new RuntimeException(e); } } }