Skip to content

Instantly share code, notes, and snippets.

@apangin
Created January 27, 2021 23:23
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save apangin/498c7a3fde345341b432cc079212166c to your computer and use it in GitHub Desktop.
Save apangin/498c7a3fde345341b432cc079212166c to your computer and use it in GitHub Desktop.
Read and modify non-manageable JVM flags in runtime: macOS version
import sun.misc.Unsafe;
import javax.management.ObjectName;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.lang.reflect.Field;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.channels.FileChannel;
import java.nio.charset.StandardCharsets;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.NoSuchElementException;
/**
* Read and modify non-manageable JVM flags in runtime
*/
public class VMFlags {
private static final long baseAddress;
private static final ByteBuffer buf;
private static final int symtab;
private static final Unsafe unsafe;
static {
try {
// Get a list of all loaded dynamic libraries
String libs = (String) ManagementFactory.getPlatformMBeanServer().invoke(
new ObjectName("com.sun.management:type=DiagnosticCommand"),
"vmDynlibs",
new Object[1],
new String[]{"[Ljava.lang.String;"}
);
// Find a line for libjvm.dylib
String libjvm = libs.lines().filter(s -> s.endsWith("libjvm.dylib")).findFirst().orElseThrow();
// Line format: 0x0000000107528000 /path/to/libjvm.dylib
String[] s = libjvm.split("\\s+", 2);
baseAddress = Long.decode(s[0]);
buf = mapFile(s[1]);
symtab = getSymtabOffset();
// Unsafe is needed to read/write memory by a direct address
Field f = Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
unsafe = (Unsafe) f.get(null);
} catch (Exception e) {
throw new UnsupportedOperationException(e);
}
}
// Map libjvm contents to memory for parsing
private static ByteBuffer mapFile(String fileName) throws IOException {
try (FileChannel ch = FileChannel.open(Paths.get(fileName), StandardOpenOption.READ)) {
ByteBuffer buf = ch.map(FileChannel.MapMode.READ_ONLY, 0, ch.size());
return buf.order(ByteOrder.nativeOrder());
}
}
// Parse Mach-O format and look for symtab_command
private static int getSymtabOffset() {
int ncmds = buf.getInt(16);
int off = 32;
for (int i = 0; i < ncmds; i++) {
if (buf.getInt(off) == 2) {
return off;
}
off += buf.getInt(off + 4);
}
throw new NoSuchElementException("Symbol table not found");
}
// Walk through the entire symtab
public static long lookup(String name) {
ByteBuffer namebuf = ByteBuffer.wrap(name.getBytes(StandardCharsets.ISO_8859_1));
int namelen = namebuf.limit();
int symoff = buf.getInt(symtab + 8);
int nsyms = buf.getInt(symtab + 12);
int stroff = buf.getInt(symtab + 16);
for (int i = 0; i < nsyms; i++) {
if ((buf.get(symoff + 4) & 0xee) == 0x0e && buf.getLong(symoff + 8) != 0) {
buf.position(stroff + buf.getInt(symoff) + 1);
if (buf.remaining() > namelen && buf.get(buf.position() + namelen) == 0
&& namebuf.equals(buf.slice().limit(namelen))) {
return buf.getLong(symoff + 8);
}
}
symoff += 16;
}
throw new NoSuchElementException("Symbol not found: " + name);
}
public static boolean getBooleanFlag(String name) {
return unsafe.getByte(baseAddress + lookup(name)) != 0;
}
public static void setBooleanFlag(String name, boolean value) {
unsafe.putByte(baseAddress + lookup(name), value ? (byte) 1 : (byte) 0);
}
public static int getIntFlag(String name) {
return unsafe.getInt(baseAddress + lookup(name));
}
public static void setIntFlag(String name, int value) {
unsafe.putInt(baseAddress + lookup(name), value);
}
public static void main(String[] args) {
System.out.println("Before -XX:-PrintCompilation");
setBooleanFlag("PrintCompilation", true);
System.out.println("After -XX:+PrintCompilation");
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment