Mercurial > hg > truffle
diff agent/src/share/classes/sun/jvm/hotspot/HotSpotTypeDataBase.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/HotSpotTypeDataBase.java Sat Dec 01 00:00:00 2007 +0000 @@ -0,0 +1,616 @@ +/* + * Copyright 2000-2005 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; + +import java.io.*; +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.types.basic.*; +import sun.jvm.hotspot.utilities.*; + +/** <P> This is the cross-platform TypeDataBase used by the Oop + hierarchy. The decision was made to make this cross-platform by + having the VM export the necessary symbols via a built-in table; + see src/share/vm/runtime/vmStructs.[ch]pp for more details. </P> + + <P> <B>WARNING</B>: clients should refer to this class through the + TypeDataBase interface and not directly to the HotSpotTypeDataBase + type. </P> + + <P> NOTE: since we are fetching the sizes of the Java primitive types + */ + +public class HotSpotTypeDataBase extends BasicTypeDataBase { + private Debugger symbolLookup; + private String[] jvmLibNames; + private static final int UNINITIALIZED_SIZE = -1; + private static final int C_INT8_SIZE = 1; + private static final int C_INT32_SIZE = 4; + private static final int C_INT64_SIZE = 8; + + private static final boolean DEBUG; + static { + DEBUG = System.getProperty("sun.jvm.hotspot.HotSpotTypeDataBase.DEBUG") + != null; + } + + /** <P> This requires a SymbolLookup mechanism as well as the + MachineDescription. Note that we do not need a NameMangler since + we use the vmStructs mechanism to avoid looking up C++ + symbols. </P> + + <P> NOTE that it is guaranteed that this constructor will not + attempt to fetch any Java values from the remote process, only C + integers and addresses. This is required because we are fetching + the sizes of the Java primitive types from the remote process, + implying that attempting to fetch them before their sizes are + known is illegal. </P> + + <P> Throws NoSuchSymbolException if a problem occurred while + looking up one of the bootstrapping symbols related to the + VMStructs table in the remote VM; this may indicate that the + remote process is not actually a HotSpot VM. </P> + */ + public HotSpotTypeDataBase(MachineDescription machDesc, + VtblAccess vtblAccess, + Debugger symbolLookup, + String[] jvmLibNames) throws NoSuchSymbolException { + super(machDesc, vtblAccess); + this.symbolLookup = symbolLookup; + this.jvmLibNames = jvmLibNames; + + readVMTypes(); + initializePrimitiveTypes(); + readVMStructs(); + readVMIntConstants(); + readVMLongConstants(); + } + + private void readVMTypes() { + // Get the variables we need in order to traverse the VMTypeEntry[] + long typeEntryTypeNameOffset; + long typeEntrySuperclassNameOffset; + long typeEntryIsOopTypeOffset; + long typeEntryIsIntegerTypeOffset; + long typeEntryIsUnsignedOffset; + long typeEntrySizeOffset; + long typeEntryArrayStride; + + typeEntryTypeNameOffset = getLongValueFromProcess("gHotSpotVMTypeEntryTypeNameOffset"); + typeEntrySuperclassNameOffset = getLongValueFromProcess("gHotSpotVMTypeEntrySuperclassNameOffset"); + typeEntryIsOopTypeOffset = getLongValueFromProcess("gHotSpotVMTypeEntryIsOopTypeOffset"); + typeEntryIsIntegerTypeOffset = getLongValueFromProcess("gHotSpotVMTypeEntryIsIntegerTypeOffset"); + typeEntryIsUnsignedOffset = getLongValueFromProcess("gHotSpotVMTypeEntryIsUnsignedOffset"); + typeEntrySizeOffset = getLongValueFromProcess("gHotSpotVMTypeEntrySizeOffset"); + typeEntryArrayStride = getLongValueFromProcess("gHotSpotVMTypeEntryArrayStride"); + + // Fetch the address of the VMTypeEntry* + Address entryAddr = lookupInProcess("gHotSpotVMTypes"); + // System.err.println("gHotSpotVMTypes address = " + entryAddr); + // Dereference this once to get the pointer to the first VMTypeEntry + // dumpMemory(entryAddr, 80); + entryAddr = entryAddr.getAddressAt(0); + + if (entryAddr == null) { + throw new RuntimeException("gHotSpotVMTypes was not initialized properly in the remote process; can not continue"); + } + + // Start iterating down it until we find an entry with no name + Address typeNameAddr = null; + do { + // Fetch the type name first + typeNameAddr = entryAddr.getAddressAt(typeEntryTypeNameOffset); + if (typeNameAddr != null) { + String typeName = CStringUtilities.getString(typeNameAddr); + + String superclassName = null; + Address superclassNameAddr = entryAddr.getAddressAt(typeEntrySuperclassNameOffset); + if (superclassNameAddr != null) { + superclassName = CStringUtilities.getString(superclassNameAddr); + } + + boolean isOopType = (entryAddr.getCIntegerAt(typeEntryIsOopTypeOffset, C_INT32_SIZE, false) != 0); + boolean isIntegerType = (entryAddr.getCIntegerAt(typeEntryIsIntegerTypeOffset, C_INT32_SIZE, false) != 0); + boolean isUnsigned = (entryAddr.getCIntegerAt(typeEntryIsUnsignedOffset, C_INT32_SIZE, false) != 0); + long size = entryAddr.getCIntegerAt(typeEntrySizeOffset, C_INT64_SIZE, true); + + createType(typeName, superclassName, isOopType, isIntegerType, isUnsigned, size); + } + + entryAddr = entryAddr.addOffsetTo(typeEntryArrayStride); + } while (typeNameAddr != null); + } + + private void initializePrimitiveTypes() { + // Look up the needed primitive types by name...they had better be present + setJBooleanType(lookupPrimitiveType("jboolean")); + setJByteType (lookupPrimitiveType("jbyte")); + setJCharType (lookupPrimitiveType("jchar")); + setJDoubleType (lookupPrimitiveType("jdouble")); + setJFloatType (lookupPrimitiveType("jfloat")); + setJIntType (lookupPrimitiveType("jint")); + setJLongType (lookupPrimitiveType("jlong")); + setJShortType (lookupPrimitiveType("jshort")); + + // Indicate that these are the Java primitive types + ((BasicType) getJBooleanType()).setIsJavaPrimitiveType(true); + ((BasicType) getJByteType()).setIsJavaPrimitiveType(true); + ((BasicType) getJCharType()).setIsJavaPrimitiveType(true); + ((BasicType) getJDoubleType()).setIsJavaPrimitiveType(true); + ((BasicType) getJFloatType()).setIsJavaPrimitiveType(true); + ((BasicType) getJIntType()).setIsJavaPrimitiveType(true); + ((BasicType) getJLongType()).setIsJavaPrimitiveType(true); + ((BasicType) getJShortType()).setIsJavaPrimitiveType(true); + } + + private Type lookupPrimitiveType(String typeName) { + Type type = lookupType(typeName, false); + if (type == null) { + throw new RuntimeException("Error initializing the HotSpotDataBase: could not find the primitive type \"" + + typeName + "\" in the remote VM's VMStructs table. This type is required in " + + "order to determine the size of Java primitive types. Can not continue."); + } + return type; + } + + private void readVMStructs() { + // Get the variables we need in order to traverse the VMStructEntry[] + long structEntryTypeNameOffset; + long structEntryFieldNameOffset; + long structEntryTypeStringOffset; + long structEntryIsStaticOffset; + long structEntryOffsetOffset; + long structEntryAddressOffset; + long structEntryArrayStride; + + structEntryTypeNameOffset = getLongValueFromProcess("gHotSpotVMStructEntryTypeNameOffset"); + structEntryFieldNameOffset = getLongValueFromProcess("gHotSpotVMStructEntryFieldNameOffset"); + structEntryTypeStringOffset = getLongValueFromProcess("gHotSpotVMStructEntryTypeStringOffset"); + structEntryIsStaticOffset = getLongValueFromProcess("gHotSpotVMStructEntryIsStaticOffset"); + structEntryOffsetOffset = getLongValueFromProcess("gHotSpotVMStructEntryOffsetOffset"); + structEntryAddressOffset = getLongValueFromProcess("gHotSpotVMStructEntryAddressOffset"); + structEntryArrayStride = getLongValueFromProcess("gHotSpotVMStructEntryArrayStride"); + + // Fetch the address of the VMStructEntry* + Address entryAddr = lookupInProcess("gHotSpotVMStructs"); + // Dereference this once to get the pointer to the first VMStructEntry + entryAddr = entryAddr.getAddressAt(0); + if (entryAddr == null) { + throw new RuntimeException("gHotSpotVMStructs was not initialized properly in the remote process; can not continue"); + } + + // Start iterating down it until we find an entry with no name + Address fieldNameAddr = null; + String typeName = null; + String fieldName = null; + String typeString = null; + boolean isStatic = false; + long offset = 0; + Address staticFieldAddr = null; + long size = 0; + long index = 0; + String opaqueName = "<opaque>"; + lookupOrCreateClass(opaqueName, false, false, false); + + do { + // Fetch the field name first + fieldNameAddr = entryAddr.getAddressAt(structEntryFieldNameOffset); + if (fieldNameAddr != null) { + fieldName = CStringUtilities.getString(fieldNameAddr); + + // Now the rest of the names. Keep in mind that the type name + // may be NULL, indicating that the type is opaque. + Address addr = entryAddr.getAddressAt(structEntryTypeNameOffset); + if (addr == null) { + throw new RuntimeException("gHotSpotVMStructs unexpectedly had a NULL type name at index " + index); + } + typeName = CStringUtilities.getString(addr); + + addr = entryAddr.getAddressAt(structEntryTypeStringOffset); + if (addr == null) { + typeString = opaqueName; + } else { + typeString = CStringUtilities.getString(addr); + } + + isStatic = !(entryAddr.getCIntegerAt(structEntryIsStaticOffset, C_INT32_SIZE, false) == 0); + if (isStatic) { + staticFieldAddr = entryAddr.getAddressAt(structEntryAddressOffset); + offset = 0; + } else { + offset = entryAddr.getCIntegerAt(structEntryOffsetOffset, C_INT64_SIZE, true); + staticFieldAddr = null; + } + + // The containing Type must already be in the database -- no exceptions + BasicType containingType = lookupOrFail(typeName); + + // The field's Type must already be in the database -- no exceptions + BasicType fieldType = lookupOrFail(typeString); + + // Create field by type + createField(containingType, fieldName, fieldType, + isStatic, offset, staticFieldAddr); + } + + ++index; + entryAddr = entryAddr.addOffsetTo(structEntryArrayStride); + } while (fieldNameAddr != null); + } + + private void readVMIntConstants() { + // Get the variables we need in order to traverse the VMIntConstantEntry[] + long intConstantEntryNameOffset; + long intConstantEntryValueOffset; + long intConstantEntryArrayStride; + + intConstantEntryNameOffset = getLongValueFromProcess("gHotSpotVMIntConstantEntryNameOffset"); + intConstantEntryValueOffset = getLongValueFromProcess("gHotSpotVMIntConstantEntryValueOffset"); + intConstantEntryArrayStride = getLongValueFromProcess("gHotSpotVMIntConstantEntryArrayStride"); + + // Fetch the address of the VMIntConstantEntry* + Address entryAddr = lookupInProcess("gHotSpotVMIntConstants"); + // Dereference this once to get the pointer to the first VMIntConstantEntry + entryAddr = entryAddr.getAddressAt(0); + if (entryAddr == null) { + throw new RuntimeException("gHotSpotVMIntConstants was not initialized properly in the remote process; can not continue"); + } + + // Start iterating down it until we find an entry with no name + Address nameAddr = null; + do { + // Fetch the type name first + nameAddr = entryAddr.getAddressAt(intConstantEntryNameOffset); + if (nameAddr != null) { + String name = CStringUtilities.getString(nameAddr); + int value = (int) entryAddr.getCIntegerAt(intConstantEntryValueOffset, C_INT32_SIZE, false); + + // Be a little resilient + Integer oldValue = lookupIntConstant(name, false); + if (oldValue == null) { + addIntConstant(name, value); + } else { + if (oldValue.intValue() != value) { + throw new RuntimeException("Error: the integer constant \"" + name + + "\" had its value redefined (old was " + oldValue + + ", new is " + value + ". Aborting."); + } else { + System.err.println("Warning: the int constant \"" + name + "\" (declared in the remote VM in VMStructs::localHotSpotVMIntConstants) " + + "had its value declared as " + value + " twice. Continuing."); + } + } + } + + entryAddr = entryAddr.addOffsetTo(intConstantEntryArrayStride); + } while (nameAddr != null); + } + + private void readVMLongConstants() { + // Get the variables we need in order to traverse the VMLongConstantEntry[] + long longConstantEntryNameOffset; + long longConstantEntryValueOffset; + long longConstantEntryArrayStride; + + longConstantEntryNameOffset = getLongValueFromProcess("gHotSpotVMLongConstantEntryNameOffset"); + longConstantEntryValueOffset = getLongValueFromProcess("gHotSpotVMLongConstantEntryValueOffset"); + longConstantEntryArrayStride = getLongValueFromProcess("gHotSpotVMLongConstantEntryArrayStride"); + + // Fetch the address of the VMLongConstantEntry* + Address entryAddr = lookupInProcess("gHotSpotVMLongConstants"); + // Dereference this once to get the pointer to the first VMLongConstantEntry + entryAddr = entryAddr.getAddressAt(0); + if (entryAddr == null) { + throw new RuntimeException("gHotSpotVMLongConstants was not initialized properly in the remote process; can not continue"); + } + + // Start iterating down it until we find an entry with no name + Address nameAddr = null; + do { + // Fetch the type name first + nameAddr = entryAddr.getAddressAt(longConstantEntryNameOffset); + if (nameAddr != null) { + String name = CStringUtilities.getString(nameAddr); + int value = (int) entryAddr.getCIntegerAt(longConstantEntryValueOffset, C_INT64_SIZE, true); + + // Be a little resilient + Long oldValue = lookupLongConstant(name, false); + if (oldValue == null) { + addLongConstant(name, value); + } else { + if (oldValue.longValue() != value) { + throw new RuntimeException("Error: the long constant \"" + name + + "\" had its value redefined (old was " + oldValue + + ", new is " + value + ". Aborting."); + } else { + System.err.println("Warning: the long constant \"" + name + "\" (declared in the remote VM in VMStructs::localHotSpotVMLongConstants) " + + "had its value declared as " + value + " twice. Continuing."); + } + } + } + + entryAddr = entryAddr.addOffsetTo(longConstantEntryArrayStride); + } while (nameAddr != null); + } + + private BasicType lookupOrFail(String typeName) { + BasicType type = (BasicType) lookupType(typeName, false); + if (type == null) { + throw new RuntimeException("Type \"" + typeName + "\", referenced in VMStructs::localHotSpotVMStructs in the remote VM, " + + "was not present in the remote VMStructs::localHotSpotVMTypes table (should have been caught " + + "in the debug build of that VM). Can not continue."); + } + return type; + } + + private long getLongValueFromProcess(String symbol) { + return lookupInProcess(symbol).getCIntegerAt(0, C_INT64_SIZE, true); + } + + private Address lookupInProcess(String symbol) throws NoSuchSymbolException { + // FIXME: abstract away the loadobject name + for (int i = 0; i < jvmLibNames.length; i++) { + Address addr = symbolLookup.lookup(jvmLibNames[i], symbol); + if (addr != null) { + return addr; + } + } + String errStr = "("; + for (int i = 0; i < jvmLibNames.length; i++) { + errStr += jvmLibNames[i]; + if (i < jvmLibNames.length - 1) { + errStr += ", "; + } + } + errStr += ")"; + throw new NoSuchSymbolException(symbol, + "Could not find symbol \"" + symbol + + "\" in any of the known library names " + + errStr); + } + + private BasicType lookupOrCreateClass(String typeName, boolean isOopType, + boolean isIntegerType, boolean isUnsigned) { + BasicType type = (BasicType) lookupType(typeName, false); + if (type == null) { + // Create a new type + type = createBasicType(typeName, isOopType, isIntegerType, isUnsigned); + } + return type; + } + + /** Creates a new BasicType, initializes its size to -1 so we can + test to ensure that all types' sizes are initialized by VMTypes, + and adds it to the database. Takes care of initializing integer + and oop types properly. */ + private BasicType createBasicType(String typeName, boolean isOopType, + boolean isIntegerType, boolean isUnsigned) { + + BasicType type = null; + + if (isIntegerType) { + type = new BasicCIntegerType(this, typeName, isUnsigned); + } else { + if (typeNameIsPointerType(typeName)) { + type = recursiveCreateBasicPointerType(typeName); + } else { + type = new BasicType(this, typeName); + } + + if (isOopType) { + // HACK: turn markOop into a C integer type. This allows + // proper handling of it in the Serviceability Agent. (FIXME + // -- consider doing something different here) + if (typeName.equals("markOop")) { + type = new BasicCIntegerType(this, typeName, true); + } else { + type.setIsOopType(true); + } + } + } + + type.setSize(UNINITIALIZED_SIZE); + addType(type); + return type; + } + + /** Recursively creates a PointerType from the string representation + of the type's name. Note that this currently needs some + workarounds due to incomplete information in the VMStructs + database. */ + private BasicPointerType recursiveCreateBasicPointerType(String typeName) { + String targetTypeName = typeName.substring(0, typeName.lastIndexOf('*')).trim(); + Type targetType = null; + if (typeNameIsPointerType(targetTypeName)) { + targetType = recursiveCreateBasicPointerType(targetTypeName); + } else { + targetType = lookupType(targetTypeName, false); + if (targetType == null) { + // Workaround for missing C integer types in database. + // Also looks like we can't throw an exception for other + // missing target types because there are some in old + // VMStructs tables that didn't have the target type declared. + // For this case, we create basic types that never get filled + // in. + + if (targetTypeName.equals("char") || + targetTypeName.equals("const char")) { + // We don't have a representation of const-ness of C types in the SA + BasicType basicTargetType = createBasicType(targetTypeName, false, true, false); + basicTargetType.setSize(1); + targetType = basicTargetType; + } else if (targetTypeName.equals("u_char")) { + BasicType basicTargetType = createBasicType(targetTypeName, false, true, true); + basicTargetType.setSize(1); + targetType = basicTargetType; + } else { + if (DEBUG) { + System.err.println("WARNING: missing target type \"" + targetTypeName + "\" for pointer type \"" + typeName + "\""); + } + targetType = createBasicType(targetTypeName, false, false, false); + } + } + } + return new BasicPointerType(this, typeName, targetType); + } + + private boolean typeNameIsPointerType(String typeName) { + int i = typeName.length() - 1; + while (i >= 0 && Character.isWhitespace(typeName.charAt(i))) { + --i; + } + if (i >= 0 && typeName.charAt(i) == '*') { + return true; + } + return false; + } + + public void createType(String typeName, String superclassName, + boolean isOopType, boolean isIntegerType, + boolean isUnsigned, long size) { + // See whether we have a superclass + BasicType superclass = null; + if (superclassName != null) { + // Fetch or create it (FIXME: would get oop types wrong if + // they had a hierarchy; consider using lookupOrFail) + superclass = lookupOrCreateClass(superclassName, false, false, false); + } + + // Lookup or create the current type + BasicType curType = lookupOrCreateClass(typeName, isOopType, isIntegerType, isUnsigned); + // Set superclass and/or ensure it's correct + if (superclass != null) { + if (curType.getSuperclass() == null) { + // Set the superclass in the current type + curType.setSuperclass(superclass); + } + + if (curType.getSuperclass() != superclass) { + throw new RuntimeException("Error: the type \"" + typeName + "\" (declared in the remote VM in VMStructs::localHotSpotVMTypes) " + + "had its superclass redefined (old was " + curType.getSuperclass().getName() + ", new is " + + superclass.getName() + ")."); + } + } + + // Classes are created with a size of UNINITIALIZED_SIZE. + // Set size if necessary. + if (curType.getSize() == UNINITIALIZED_SIZE) { + curType.setSize(size); + } else { + if (curType.getSize() != size) { + throw new RuntimeException("Error: the type \"" + typeName + "\" (declared in the remote VM in VMStructs::localHotSpotVMTypes) " + + "had its size redefined (old was " + curType.getSize() + ", new is " + size + ")."); + } + + System.err.println("Warning: the type \"" + typeName + "\" (declared in the remote VM in VMStructs::localHotSpotVMTypes) " + + "had its size declared as " + size + " twice. Continuing."); + } + + } + + /** "Virtual constructor" for fields based on type */ + public void createField(BasicType containingType, + String name, Type type, boolean isStatic, + long offset, Address staticFieldAddress) { + // Add field to containing type + containingType.addField(internalCreateField(containingType, name, type, isStatic, offset, staticFieldAddress)); + } + + Field internalCreateField(BasicType containingType, + String name, Type type, boolean isStatic, + long offset, Address staticFieldAddress) { + // "Virtual constructor" based on type + if (type.isOopType()) { + return new BasicOopField(this, containingType, name, type, + isStatic, offset, staticFieldAddress); + } + + if (type instanceof CIntegerType) { + return new BasicCIntegerField(this, containingType, name, type, + isStatic, offset, staticFieldAddress); + } + + if (type.equals(getJBooleanType())) { + return new BasicJBooleanField(this, containingType, name, type, + isStatic, offset, staticFieldAddress); + } + + if (type.equals(getJByteType())) { + return new BasicJByteField(this, containingType, name, type, + isStatic, offset, staticFieldAddress); + } + + if (type.equals(getJCharType())) { + return new BasicJCharField(this, containingType, name, type, + isStatic, offset, staticFieldAddress); + } + + if (type.equals(getJDoubleType())) { + return new BasicJDoubleField(this, containingType, name, type, + isStatic, offset, staticFieldAddress); + } + + if (type.equals(getJFloatType())) { + return new BasicJFloatField(this, containingType, name, type, + isStatic, offset, staticFieldAddress); + } + + if (type.equals(getJIntType())) { + return new BasicJIntField(this, containingType, name, type, + isStatic, offset, staticFieldAddress); + } + + if (type.equals(getJLongType())) { + return new BasicJLongField(this, containingType, name, type, + isStatic, offset, staticFieldAddress); + } + + if (type.equals(getJShortType())) { + return new BasicJShortField(this, containingType, name, type, + isStatic, offset, staticFieldAddress); + } + + // Unknown ("opaque") type. Instantiate ordinary Field. + return new BasicField(this, containingType, name, type, + isStatic, offset, staticFieldAddress); + } + + // For debugging + private void dumpMemory(Address addr, int len) { + int i = 0; + while (i < len) { + System.err.print(addr.addOffsetTo(i) + ":"); + for (int j = 0; j < 8 && i < len; i++, j++) { + String s = Long.toHexString(addr.getCIntegerAt(i, 1, true)); + System.err.print(" 0x"); + for (int k = 0; k < 2 - s.length(); k++) { + System.err.print("0"); + } + System.err.print(s); + } + System.err.println(); + } + } +}