Created
January 27, 2021 23:23
-
-
Save apangin/498c7a3fde345341b432cc079212166c to your computer and use it in GitHub Desktop.
Read and modify non-manageable JVM flags in runtime: macOS version
This file contains hidden or 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
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