Mercurial > hg > truffle
diff agent/src/share/classes/sun/jvm/hotspot/runtime/VM.java @ 0:a61af66fc99e jdk7-b24
Initial load
author | duke |
---|---|
date | Sat, 01 Dec 2007 00:00:00 +0000 |
parents | |
children | ba764ed4b6f2 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/agent/src/share/classes/sun/jvm/hotspot/runtime/VM.java Sat Dec 01 00:00:00 2007 +0000 @@ -0,0 +1,795 @@ +/* + * Copyright 2000-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.runtime; + +import java.io.*; +import java.net.*; +import java.util.*; +import java.util.regex.*; +import sun.jvm.hotspot.code.*; +import sun.jvm.hotspot.c1.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.interpreter.*; +import sun.jvm.hotspot.memory.*; +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.utilities.*; + +/** <P> This class encapsulates the global state of the VM; the + universe, object heap, interpreter, etc. It is a Singleton and + must be initialized with a call to initialize() before calling + getVM(). </P> + + <P> Many auxiliary classes (i.e., most of the VMObjects) keep + needed field offsets in the form of static Field objects. In a + debugging system, the VM might be shutdown and re-initialized (on + a differently-configured build, i.e., 32- vs. 64-bit), and all old + cached state (including fields and field offsets) must be + flushed. </P> + + <P> An Observer pattern is used to implement the initialization of + such classes. Each such class, in its static initializer, + registers an Observer with the VM class via + VM.registerVMInitializedObserver(). This Observer is guaranteed to + be notified whenever the VM is initialized (or re-initialized). To + implement the first-time initialization, the observer is also + notified when it registers itself with the VM. (For bootstrapping + reasons, this implies that the constructor of VM can not + instantiate any such objects, since VM.soleInstance will not have + been set yet. This is a bootstrapping issue which may have to be + revisited later.) </P> +*/ + +public class VM { + private static VM soleInstance; + private static List vmInitializedObservers = new ArrayList(); + private List vmResumedObservers = new ArrayList(); + private List vmSuspendedObservers = new ArrayList(); + private TypeDataBase db; + private boolean isBigEndian; + /** This is only present if in a debugging system */ + private JVMDebugger debugger; + private long stackBias; + private long logAddressSize; + private Universe universe; + private ObjectHeap heap; + private SymbolTable symbols; + private StringTable strings; + private SystemDictionary dict; + private Threads threads; + private ObjectSynchronizer synchronizer; + private JNIHandles handles; + private Interpreter interpreter; + private StubRoutines stubRoutines; + private Bytes bytes; + /** Flags indicating whether we are attached to a core, C1, or C2 build */ + private boolean usingClientCompiler; + private boolean usingServerCompiler; + /** Flag indicating whether UseTLAB is turned on */ + private boolean useTLAB; + /** alignment constants */ + private boolean isLP64; + private int bytesPerLong; + private int minObjAlignmentInBytes; + /** This is only present in a non-core build */ + private CodeCache codeCache; + /** This is only present in a C1 build */ + private Runtime1 runtime1; + /** These constants come from globalDefinitions.hpp */ + private int invocationEntryBCI; + private int invalidOSREntryBCI; + private ReversePtrs revPtrs; + private VMRegImpl vmregImpl; + + // System.getProperties from debuggee VM + private Properties sysProps; + + // VM version strings come from Abstract_VM_Version class + private String vmRelease; + private String vmInternalInfo; + + private Flag[] commandLineFlags; + private Map flagsMap; + + private static Type intxType; + private static Type uintxType; + private static CIntegerType boolType; + private Boolean sharingEnabled; + + // command line flags supplied to VM - see struct Flag in globals.hpp + public static final class Flag { + private String type; + private String name; + private Address addr; + private String kind; + + private Flag(String type, String name, Address addr, String kind) { + this.type = type; + this.name = name; + this.addr = addr; + this.kind = kind; + } + + public String getType() { + return type; + } + + public String getName() { + return name; + } + + public Address getAddress() { + return addr; + } + + public String getKind() { + return kind; + } + + public boolean isBool() { + return type.equals("bool"); + } + + public boolean getBool() { + if (Assert.ASSERTS_ENABLED) { + Assert.that(isBool(), "not a bool flag!"); + } + return addr.getCIntegerAt(0, boolType.getSize(), boolType.isUnsigned()) + != 0; + } + + public boolean isIntx() { + return type.equals("intx"); + } + + public long getIntx() { + if (Assert.ASSERTS_ENABLED) { + Assert.that(isIntx(), "not a intx flag!"); + } + return addr.getCIntegerAt(0, intxType.getSize(), false); + } + + public boolean isUIntx() { + return type.equals("uintx"); + } + + public long getUIntx() { + if (Assert.ASSERTS_ENABLED) { + Assert.that(isUIntx(), "not a uintx flag!"); + } + return addr.getCIntegerAt(0, uintxType.getSize(), true); + } + + public String getValue() { + if (isBool()) { + return new Boolean(getBool()).toString(); + } else if (isIntx()) { + return new Long(getIntx()).toString(); + } else if (isUIntx()) { + return new Long(getUIntx()).toString(); + } else { + return null; + } + } + }; + + private static void checkVMVersion(String vmRelease) { + if (System.getProperty("sun.jvm.hotspot.runtime.VM.disableVersionCheck") == null) { + // read sa build version. + String versionProp = "sun.jvm.hotspot.runtime.VM.saBuildVersion"; + String saVersion = saProps.getProperty(versionProp); + if (saVersion == null) + throw new RuntimeException("Missing property " + versionProp); + + // Strip nonproduct VM version substring (note: saVersion doesn't have it). + String vmVersion = vmRelease.replaceAll("(-fastdebug)|(-debug)|(-jvmg)|(-optimized)|(-profiled)",""); + + if (saVersion.equals(vmVersion)) { + // Exact match + return; + } + if (saVersion.indexOf('-') == saVersion.lastIndexOf('-') && + vmVersion.indexOf('-') == vmVersion.lastIndexOf('-')) { + // Throw exception if different release versions: + // <major>.<minor>-b<n> + throw new VMVersionMismatchException(saVersion, vmRelease); + } else { + // Otherwise print warning to allow mismatch not release versions + // during development. + System.err.println("WARNING: Hotspot VM version " + vmRelease + + " does not match with SA version " + saVersion + + "." + " You may see unexpected results. "); + } + } else { + System.err.println("WARNING: You have disabled SA and VM version check. You may be " + + "using incompatible version of SA and you may see unexpected " + + "results."); + } + } + + private static final boolean disableDerivedPrinterTableCheck; + private static final Properties saProps; + + static { + saProps = new Properties(); + URL url = null; + try { + url = VM.class.getClassLoader().getResource("sa.properties"); + saProps.load(new BufferedInputStream(url.openStream())); + } catch (Exception e) { + throw new RuntimeException("Unable to load properties " + + (url == null ? "null" : url.toString()) + + ": " + e.getMessage()); + } + + disableDerivedPrinterTableCheck = System.getProperty("sun.jvm.hotspot.runtime.VM.disableDerivedPointerTableCheck") != null; + } + + private VM(TypeDataBase db, JVMDebugger debugger, boolean isBigEndian) { + this.db = db; + this.debugger = debugger; + this.isBigEndian = isBigEndian; + + // Note that we don't construct universe, heap, threads, + // interpreter, or stubRoutines here (any more). The current + // initialization mechanisms require that the VM be completely set + // up (i.e., out of its constructor, with soleInstance assigned) + // before their static initializers are run. + + if (db.getAddressSize() == 4) { + logAddressSize = 2; + } else if (db.getAddressSize() == 8) { + logAddressSize = 3; + } else { + throw new RuntimeException("Address size " + db.getAddressSize() + " not yet supported"); + } + + // read VM version info + try { + Type vmVersion = db.lookupType("Abstract_VM_Version"); + Address releaseAddr = vmVersion.getAddressField("_s_vm_release").getValue(); + vmRelease = CStringUtilities.getString(releaseAddr); + Address vmInternalInfoAddr = vmVersion.getAddressField("_s_internal_vm_info_string").getValue(); + vmInternalInfo = CStringUtilities.getString(vmInternalInfoAddr); + } catch (Exception exp) { + throw new RuntimeException("can't determine target's VM version : " + exp.getMessage()); + } + + checkVMVersion(vmRelease); + + stackBias = db.lookupIntConstant("STACK_BIAS").intValue(); + invocationEntryBCI = db.lookupIntConstant("InvocationEntryBci").intValue(); + invalidOSREntryBCI = db.lookupIntConstant("InvalidOSREntryBci").intValue(); + + // We infer the presence of C1 or C2 from a couple of fields we + // already have present in the type database + { + Type type = db.lookupType("methodOopDesc"); + if (type.getField("_from_compiled_entry", false, false) == null) { + // Neither C1 nor C2 is present + usingClientCompiler = false; + usingServerCompiler = false; + } else { + // Determine whether C2 is present + if (type.getField("_interpreter_invocation_count", false, false) != null) { + usingServerCompiler = true; + } else { + usingClientCompiler = true; + } + } + } + + useTLAB = (db.lookupIntConstant("UseTLAB").intValue() != 0); + + if (debugger != null) { + isLP64 = debugger.getMachineDescription().isLP64(); + } + bytesPerLong = db.lookupIntConstant("BytesPerLong").intValue(); + minObjAlignmentInBytes = db.lookupIntConstant("MinObjAlignmentInBytes").intValue(); + + intxType = db.lookupType("intx"); + uintxType = db.lookupType("uintx"); + boolType = (CIntegerType) db.lookupType("bool"); + } + + /** This could be used by a reflective runtime system */ + public static void initialize(TypeDataBase db, boolean isBigEndian) { + if (soleInstance != null) { + throw new RuntimeException("Attempt to initialize VM twice"); + } + soleInstance = new VM(db, null, isBigEndian); + for (Iterator iter = vmInitializedObservers.iterator(); iter.hasNext(); ) { + ((Observer) iter.next()).update(null, null); + } + } + + /** This is used by the debugging system */ + public static void initialize(TypeDataBase db, JVMDebugger debugger) { + if (soleInstance != null) { + throw new RuntimeException("Attempt to initialize VM twice"); + } + soleInstance = new VM(db, debugger, debugger.getMachineDescription().isBigEndian()); + for (Iterator iter = vmInitializedObservers.iterator(); iter.hasNext(); ) { + ((Observer) iter.next()).update(null, null); + } + } + + /** This is used by the debugging system */ + public static void shutdown() { + soleInstance = null; + } + + /** This is used by both the debugger and any runtime system. It is + the basic mechanism by which classes which mimic underlying VM + functionality cause themselves to be initialized. The given + observer will be notified (with arguments (null, null)) when the + VM is re-initialized, as well as when it registers itself with + the VM. */ + public static void registerVMInitializedObserver(Observer o) { + vmInitializedObservers.add(o); + o.update(null, null); + } + + /** This is the primary accessor used by both the debugger and any + potential runtime system */ + public static VM getVM() { + if (soleInstance == null) { + throw new RuntimeException("VM.initialize() was not yet called"); + } + return soleInstance; + } + + /** This is only used by the debugging system. The given observer + will be notified if the underlying VM resumes execution. NOTE + that the given observer is not triggered if the VM is currently + running and therefore differs in behavior from {@link + #registerVMInitializedObserver} (because of the possibility of + race conditions if the observer is added while the VM is being + suspended or resumed). */ + public void registerVMResumedObserver(Observer o) { + vmResumedObservers.add(o); + } + + /** This is only used by the debugging system. The given observer + will be notified if the underlying VM suspends execution. NOTE + that the given observer is not triggered if the VM is currently + suspended and therefore differs in behavior from {@link + #registerVMInitializedObserver} (because of the possibility of + race conditions if the observer is added while the VM is being + suspended or resumed). */ + public void registerVMSuspendedObserver(Observer o) { + vmSuspendedObservers.add(o); + } + + /** This is only used by the debugging system. Informs all + registered resumption observers that the VM has been resumed. + The application is responsible for actually having performed the + resumption. No OopHandles must be used after this point, as they + may move in the target address space due to garbage + collection. */ + public void fireVMResumed() { + for (Iterator iter = vmResumedObservers.iterator(); iter.hasNext(); ) { + ((Observer) iter.next()).update(null, null); + } + } + + /** This is only used by the debugging system. Informs all + registered suspension observers that the VM has been suspended. + The application is responsible for actually having performed the + suspension. Garbage collection must be forbidden at this point; + for example, a JPDA-level suspension is not adequate since the + VM thread may still be running. */ + public void fireVMSuspended() { + for (Iterator iter = vmSuspendedObservers.iterator(); iter.hasNext(); ) { + ((Observer) iter.next()).update(null, null); + } + } + + /** Returns the OS this VM is running on. Notice that by delegating + to the debugger we can transparently support remote + debugging. */ + public String getOS() { + if (debugger != null) { + return debugger.getOS(); + } + return PlatformInfo.getOS(); + } + + /** Returns the CPU this VM is running on. Notice that by delegating + to the debugger we can transparently support remote + debugging. */ + public String getCPU() { + if (debugger != null) { + return debugger.getCPU(); + } + return PlatformInfo.getCPU(); + } + + public Type lookupType(String cTypeName) { + return db.lookupType(cTypeName); + } + + public Integer lookupIntConstant(String name) { + return db.lookupIntConstant(name); + } + + public long getAddressSize() { + return db.getAddressSize(); + } + + public long getOopSize() { + return db.getOopSize(); + } + + public long getLogAddressSize() { + return logAddressSize; + } + + /** NOTE: this offset is in BYTES in this system! */ + public long getStackBias() { + return stackBias; + } + + /** Indicates whether the underlying machine supports the LP64 data + model. This is needed for conditionalizing code in a few places */ + public boolean isLP64() { + if (Assert.ASSERTS_ENABLED) { + Assert.that(isDebugging(), "Debugging system only for now"); + } + return isLP64; + } + + /** Get bytes-per-long == long/double natural alignment. */ + public int getBytesPerLong() { + return bytesPerLong; + } + + /** Get minimum object alignment in bytes. */ + public int getMinObjAlignmentInBytes() { + return minObjAlignmentInBytes; + } + + /** Utility routine for getting data structure alignment correct */ + public long alignUp(long size, long alignment) { + return (size + alignment - 1) & ~(alignment - 1); + } + + /** Utility routine for getting data structure alignment correct */ + public long alignDown(long size, long alignment) { + return size & ~(alignment - 1); + } + + /** Utility routine for building an int from two "unsigned" 16-bit + shorts */ + public int buildIntFromShorts(short low, short high) { + return (((int) high) << 16) | (((int) low) & 0xFFFF); + } + + /** Utility routine for building a long from two "unsigned" 32-bit + ints in <b>platform-dependent</b> order */ + public long buildLongFromIntsPD(int oneHalf, int otherHalf) { + if (isBigEndian) { + return (((long) otherHalf) << 32) | (((long) oneHalf) & 0x00000000FFFFFFFFL); + } else{ + return (((long) oneHalf) << 32) | (((long) otherHalf) & 0x00000000FFFFFFFFL); + } + } + + /** Indicates whether Thread-Local Allocation Buffers are used */ + public boolean getUseTLAB() { + return useTLAB; + } + + public TypeDataBase getTypeDataBase() { + return db; + } + + public Universe getUniverse() { + if (universe == null) { + universe = new Universe(); + } + return universe; + } + + public ObjectHeap getObjectHeap() { + if (heap == null) { + heap = new ObjectHeap(db); + } + return heap; + } + + public SymbolTable getSymbolTable() { + if (symbols == null) { + symbols = SymbolTable.getTheTable(); + } + return symbols; + } + + public StringTable getStringTable() { + if (strings == null) { + strings = StringTable.getTheTable(); + } + return strings; + } + + public SystemDictionary getSystemDictionary() { + if (dict == null) { + dict = new SystemDictionary(); + } + return dict; + } + + public Threads getThreads() { + if (threads == null) { + threads = new Threads(); + } + return threads; + } + + public ObjectSynchronizer getObjectSynchronizer() { + if (synchronizer == null) { + synchronizer = new ObjectSynchronizer(); + } + return synchronizer; + } + + public JNIHandles getJNIHandles() { + if (handles == null) { + handles = new JNIHandles(); + } + return handles; + } + + public Interpreter getInterpreter() { + if (interpreter == null) { + interpreter = new Interpreter(); + } + return interpreter; + } + + public StubRoutines getStubRoutines() { + if (stubRoutines == null) { + stubRoutines = new StubRoutines(); + } + return stubRoutines; + } + + public VMRegImpl getVMRegImplInfo() { + if (vmregImpl == null) { + vmregImpl = new VMRegImpl(); + } + return vmregImpl; + } + + public Bytes getBytes() { + if (bytes == null) { + bytes = new Bytes(debugger.getMachineDescription()); + } + return bytes; + } + + /** Returns true if this is a "core" build, false if either C1 or C2 + is present */ + public boolean isCore() { + return (!(usingClientCompiler || usingServerCompiler)); + } + + /** Returns true if this is a C1 build, false otherwise */ + public boolean isClientCompiler() { + return usingClientCompiler; + } + + /** Returns true if this is a C2 build, false otherwise */ + public boolean isServerCompiler() { + return usingServerCompiler; + } + + /** Returns true if C2 derived pointer table should be used, false otherwise */ + public boolean useDerivedPointerTable() { + return !disableDerivedPrinterTableCheck; + } + + /** Returns the code cache; should not be used if is core build */ + public CodeCache getCodeCache() { + if (Assert.ASSERTS_ENABLED) { + Assert.that(!isCore(), "noncore builds only"); + } + if (codeCache == null) { + codeCache = new CodeCache(); + } + return codeCache; + } + + /** Should only be called for C1 builds */ + public Runtime1 getRuntime1() { + if (Assert.ASSERTS_ENABLED) { + Assert.that(isClientCompiler(), "C1 builds only"); + } + if (runtime1 == null) { + runtime1 = new Runtime1(); + } + return runtime1; + } + + /** Test to see whether we're in debugging mode (NOTE: this really + should not be tested by this code; currently only used in + StackFrameStream) */ + public boolean isDebugging() { + return (debugger != null); + } + + /** This is only used by the debugging (i.e., non-runtime) system */ + public JVMDebugger getDebugger() { + if (debugger == null) { + throw new RuntimeException("Attempt to use debugger in runtime system"); + } + return debugger; + } + + /** Indicates whether a given program counter is in Java code. This + includes but is not spanned by the interpreter and code cache. + Only used in the debugging system, for implementing + JavaThread.currentFrameGuess() on x86. */ + public boolean isJavaPCDbg(Address addr) { + // FIXME: this is not a complete enough set: must include areas + // like vtable stubs + return (getInterpreter().contains(addr) || + getCodeCache().contains(addr)); + } + + /** FIXME: figure out where to stick this */ + public int getInvocationEntryBCI() { + return invocationEntryBCI; + } + + /** FIXME: figure out where to stick this */ + public int getInvalidOSREntryBCI() { + return invalidOSREntryBCI; + } + + // FIXME: figure out where to stick this + public boolean wizardMode() { + return true; + } + + public ReversePtrs getRevPtrs() { + return revPtrs; + } + + public void setRevPtrs(ReversePtrs rp) { + revPtrs = rp; + } + + // returns null, if not available. + public String getVMRelease() { + return vmRelease; + } + + // returns null, if not available. + public String getVMInternalInfo() { + return vmInternalInfo; + } + + public boolean isSharingEnabled() { + if (sharingEnabled == null) { + Flag flag = getCommandLineFlag("UseSharedSpaces"); + sharingEnabled = (flag == null)? Boolean.FALSE : + (flag.getBool()? Boolean.TRUE: Boolean.FALSE); + } + return sharingEnabled.booleanValue(); + } + + + // returns null, if not available. + public Flag[] getCommandLineFlags() { + if (commandLineFlags == null) { + readCommandLineFlags(); + } + + return commandLineFlags; + } + + public Flag getCommandLineFlag(String name) { + if (flagsMap == null) { + flagsMap = new HashMap(); + Flag[] flags = getCommandLineFlags(); + for (int i = 0; i < flags.length; i++) { + flagsMap.put(flags[i].getName(), flags[i]); + } + } + return (Flag) flagsMap.get(name); + } + + private void readCommandLineFlags() { + // get command line flags + TypeDataBase db = getTypeDataBase(); + try { + Type flagType = db.lookupType("Flag"); + int numFlags = (int) flagType.getCIntegerField("numFlags").getValue(); + // NOTE: last flag contains null values. + commandLineFlags = new Flag[numFlags - 1]; + + Address flagAddr = flagType.getAddressField("flags").getValue(); + + AddressField typeFld = flagType.getAddressField("type"); + AddressField nameFld = flagType.getAddressField("name"); + AddressField addrFld = flagType.getAddressField("addr"); + AddressField kindFld = flagType.getAddressField("kind"); + + long flagSize = flagType.getSize(); // sizeof(Flag) + + // NOTE: last flag contains null values. + for (int f = 0; f < numFlags - 1; f++) { + String type = CStringUtilities.getString(typeFld.getValue(flagAddr)); + String name = CStringUtilities.getString(nameFld.getValue(flagAddr)); + Address addr = addrFld.getValue(flagAddr); + String kind = CStringUtilities.getString(kindFld.getValue(flagAddr)); + commandLineFlags[f] = new Flag(type, name, addr, kind); + flagAddr = flagAddr.addOffsetTo(flagSize); + } + + // sort flags by name + Arrays.sort(commandLineFlags, new Comparator() { + public int compare(Object o1, Object o2) { + Flag f1 = (Flag) o1; + Flag f2 = (Flag) o2; + return f1.getName().compareTo(f2.getName()); + } + }); + } catch (Exception exp) { + // ignore. may be older version. command line flags not available. + } + } + + public String getSystemProperty(String key) { + Properties props = getSystemProperties(); + return (props != null)? props.getProperty(key) : null; + } + + public Properties getSystemProperties() { + if (sysProps == null) { + readSystemProperties(); + } + return sysProps; + } + + private void readSystemProperties() { + InstanceKlass systemKls = getSystemDictionary().getSystemKlass(); + systemKls.iterate(new DefaultOopVisitor() { + ObjectReader objReader = new ObjectReader(); + public void doOop(sun.jvm.hotspot.oops.OopField field, boolean isVMField) { + if (field.getID().getName().equals("props")) { + try { + sysProps = (Properties) objReader.readObject(field.getValue(getObj())); + } catch (Exception e) { + if (Assert.ASSERTS_ENABLED) { + e.printStackTrace(); + } + } + } + } + }, false); + } +}