Last active
August 29, 2015 14:09
-
-
Save serkan-ozal/7daca297fd79b2d1a32e to your computer and use it in GitHub Desktop.
Hotspot serviceability agent based compressed-oops mode finder that attaches to current JVM process as different process to get information
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* @author SERKAN OZAL | |
* | |
* E-Mail: <a href="mailto:[email protected]">[email protected]</a> | |
* GitHub: <a>https://github.com/serkan-ozal</a> | |
*/ | |
package tr.com.serkanozal.jillegal.util.compressedoops.hotspot; | |
import java.io.BufferedReader; | |
import java.io.File; | |
import java.io.InputStreamReader; | |
import java.lang.management.ManagementFactory; | |
import java.lang.management.RuntimeMXBean; | |
import java.lang.reflect.Field; | |
import java.lang.reflect.Method; | |
import java.net.URL; | |
import java.util.ArrayList; | |
import java.util.List; | |
import java.util.Set; | |
import org.apache.log4j.Logger; | |
import sun.jvm.hotspot.HotSpotAgent; | |
import sun.jvm.hotspot.memory.Universe; | |
import sun.jvm.hotspot.runtime.VM; | |
import sun.management.VMManagement; | |
import sun.misc.Unsafe; | |
import tr.com.serkanozal.jillegal.util.ClasspathUtil; | |
import tr.com.serkanozal.jillegal.util.compressedoops.CompressedOopsInfo; | |
public class HotspotServiceabilityAgentBasedCompressedOopsInfoProvider implements HotspotCompressedOopsInfoProvider { | |
private static final Logger logger = | |
Logger.getLogger(HotspotServiceabilityAgentBasedCompressedOopsInfoProvider.class); | |
private static final String SYSTEM_CLASSLOADER_VM_ARGUMENT_NAME = "java.system.class.loader"; | |
private static final String JVM_INFO_RETRIEVE_START = "$$$_JVM_INFO_RETRIEVE_START_$$$"; | |
private static final String JVM_INFO_RETRIEVE_FINISH = "$$$_JVM_INFO_RETRIEVE_FINISH_$$$"; | |
private static final String JVM_INFO_LINE = "$$$_JVM_INFO_LINE_$$$"; | |
private static final String NARROW_OOP_BASE_INFO_KEY = "NARROW_OOP_BASE_INFO"; | |
private static final String NARROW_OOP_SHIFT_INFO_KEY = "NARROW_OOP_SHIFT_INFO"; | |
private static final String NARROW_KLASS_BASE_INFO_KEY = "NARROW_KLASS_BASE_INFO"; | |
private static final String NARROW_KLASS_SHIFT_INFO_KEY = "NARROW_KLASS_SHIFT_INFO"; | |
@Override | |
public CompressedOopsInfo getCompressedOopsInfo(Unsafe unsafe, int oopSize, | |
int addressSize, int objectAlignment, boolean isCompressedRef) { | |
if (isHotspotJvm()) { | |
return findCompressedOopsInfo(findProcessId()); | |
} | |
else { | |
return null; | |
} | |
} | |
private boolean isHotspotJvm() { | |
String name = System.getProperty("java.vm.name").toLowerCase(); | |
return name.contains("hotspot") || name.contains("openjdk"); | |
} | |
private CompressedOopsInfo findCompressedOopsInfo(int processId) { | |
if (processId == 0) { | |
return null; | |
} | |
try { | |
long narrowOopBase = -1; | |
int narrowOopShift = -1; | |
long narrowKlassBase = 0; | |
int narrowKlassShift = 0; | |
StringBuilder cpBuilder = new StringBuilder(); | |
Set<URL> cpUrls = ClasspathUtil.getClasspathUrls(); | |
for (URL cpUrl : cpUrls) { | |
String cpUrlPath = cpUrl.getPath(); | |
if (cpUrlPath.contains("sa_jdi")) { | |
continue; | |
} | |
cpBuilder.append(cpUrlPath).append(File.pathSeparator); | |
} | |
String classpath = cpBuilder.toString().replace("%20", " "); | |
List<String> args = new ArrayList<String>(); | |
args.add(System.getProperty("java.home") + "/" + "bin" + "/" + "java"); | |
args.add("-cp"); | |
args.add(classpath); | |
args.add("-D" + SYSTEM_CLASSLOADER_VM_ARGUMENT_NAME + "=" + HotspotJvmAwareSaJdiClassLoader.class.getName()); | |
args.add("-D" + HotspotJvmAwareSaJdiClassLoader.URL_CLASSPATH_VM_ARGUMENT_NAME + "=" + cpUrls); | |
args.add(HotspotServiceabilityAgentBasedCompressedOopsInfoProvider.class.getName()); | |
args.add(String.valueOf(processId)); | |
Process p = new ProcessBuilder(args).start(); | |
BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream())); | |
for (String line = br.readLine(); line != null; line = br.readLine()) { | |
// System.out.println(line); | |
String[] parts = line.split("[\\s]+"); | |
if (JVM_INFO_LINE.equals(parts[0])) { | |
if (NARROW_OOP_BASE_INFO_KEY.equals(parts[1])) { | |
narrowOopBase = Long.parseLong(parts[2]); | |
} | |
else if (NARROW_OOP_SHIFT_INFO_KEY.equals(parts[1])) { | |
narrowOopShift = Integer.parseInt(parts[2]); | |
} | |
else if (NARROW_KLASS_BASE_INFO_KEY.equals(parts[1])) { | |
narrowKlassBase = Long.parseLong(parts[2]); | |
} | |
else if (NARROW_KLASS_SHIFT_INFO_KEY.equals(parts[1])) { | |
narrowKlassShift = Integer.parseInt(parts[2]); | |
} | |
} | |
else if (JVM_INFO_RETRIEVE_FINISH.equals(line.trim())) { | |
break; | |
} | |
} | |
BufferedReader brErr = new BufferedReader(new InputStreamReader(p.getErrorStream())); | |
for (String line = brErr.readLine(); line != null; line = brErr.readLine()) { | |
System.err.println(line); | |
} | |
p.destroy(); | |
System.out.println("Narrow oop base : " + narrowOopBase); | |
System.out.println("Narrow oop shift : " + narrowOopShift); | |
System.out.println("Narrow klass base : " + narrowKlassBase); | |
System.out.println("Narrow klass shift : " + narrowKlassShift); | |
if (narrowOopBase != -1 && narrowOopShift != -1) { | |
logger.info("Narrow oop base : " + narrowOopBase); | |
logger.info("Narrow oop shift : " + narrowOopShift); | |
logger.info("Narrow klass base : " + narrowKlassBase); | |
logger.info("Narrow klass shift : " + narrowKlassShift); | |
return new CompressedOopsInfo(narrowOopBase, narrowOopShift, narrowKlassBase, narrowKlassShift); | |
} | |
else { | |
return null; | |
} | |
} | |
catch (Throwable t) { | |
logger.error("Unable to find JVM Info", t); | |
return null; | |
} | |
} | |
private int findProcessId() { | |
try { | |
RuntimeMXBean mxbean = ManagementFactory.getRuntimeMXBean(); | |
Field jvmField = mxbean.getClass().getDeclaredField("jvm"); | |
jvmField.setAccessible(true); | |
VMManagement management = (VMManagement) jvmField.get(mxbean); | |
Method method = management.getClass().getDeclaredMethod("getProcessId"); | |
method.setAccessible(true); | |
return (Integer) method.invoke(management); | |
} | |
catch (Throwable t) { | |
logger.error("Unable to find current process id", t); | |
return 0; | |
} | |
} | |
public static void main(final String[] args) throws InterruptedException { | |
try { | |
System.out.println(JVM_INFO_RETRIEVE_START); | |
System.setProperty("sun.jvm.hotspot.debugger.useProcDebugger", "true"); | |
System.setProperty("sun.jvm.hotspot.debugger.useWindbgDebugger", "true"); | |
System.setProperty("sun.jvm.hotspot.runtime.VM.disableVersionCheck", "true"); | |
final HotSpotAgent agent = new HotSpotAgent(); | |
Thread t = new Thread() { | |
public void run() { | |
agent.attach(Integer.parseInt(args[0])); | |
}; | |
}; | |
t.start(); | |
boolean vmInitialized = false; | |
// Check five times :) | |
for (int i = 0; i < 5; i++) { | |
Thread.sleep(1000); | |
try { | |
if (VM.getVM() != null) { | |
vmInitialized = true; | |
break; | |
} | |
} | |
catch (Throwable err) { | |
} | |
} | |
if (vmInitialized) { | |
try { | |
System.out.println(JVM_INFO_LINE + " " + NARROW_OOP_BASE_INFO_KEY + " " + Universe.getNarrowOopBase()); | |
System.out.println(JVM_INFO_LINE + " " + NARROW_OOP_SHIFT_INFO_KEY + " " + Universe.getNarrowOopShift()); | |
System.out.println(JVM_INFO_LINE + " " + NARROW_KLASS_BASE_INFO_KEY + " " + Universe.getNarrowKlassBase()); | |
System.out.println(JVM_INFO_LINE + " " + NARROW_KLASS_SHIFT_INFO_KEY + " " + Universe.getNarrowKlassShift()); | |
} | |
catch (Throwable e) { | |
e.printStackTrace(System.out); | |
} | |
} | |
else { | |
System.out.println("VM couldn't be initialized !"); | |
} | |
agent.detach(); | |
System.out.println(JVM_INFO_RETRIEVE_FINISH); | |
} | |
catch (Throwable t) { | |
t.printStackTrace(System.out); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment