Mercurial > hg > truffle
diff agent/src/share/classes/sun/jvm/hotspot/jdi/VirtualMachineImpl.java @ 0:a61af66fc99e jdk7-b24
Initial load
author | duke |
---|---|
date | Sat, 01 Dec 2007 00:00:00 +0000 |
parents | |
children | c18cbe5936b8 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/agent/src/share/classes/sun/jvm/hotspot/jdi/VirtualMachineImpl.java Sat Dec 01 00:00:00 2007 +0000 @@ -0,0 +1,1232 @@ +/* + * Copyright 2002-2006 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.jdi; + +import com.sun.jdi.*; +import com.sun.jdi.event.EventQueue; +import com.sun.jdi.request.EventRequestManager; + +import sun.jvm.hotspot.HotSpotAgent; +import sun.jvm.hotspot.types.TypeDataBase; +import sun.jvm.hotspot.oops.Klass; +import sun.jvm.hotspot.oops.InstanceKlass; +import sun.jvm.hotspot.oops.ArrayKlass; +import sun.jvm.hotspot.oops.ObjArrayKlass; +import sun.jvm.hotspot.oops.TypeArrayKlass; +import sun.jvm.hotspot.oops.Oop; +import sun.jvm.hotspot.oops.Instance; +import sun.jvm.hotspot.oops.Array; +import sun.jvm.hotspot.oops.ObjArray; +import sun.jvm.hotspot.oops.TypeArray; +import sun.jvm.hotspot.oops.Symbol; +import sun.jvm.hotspot.oops.ObjectHeap; +import sun.jvm.hotspot.oops.DefaultHeapVisitor; +import sun.jvm.hotspot.oops.JVMDIClassStatus; +import sun.jvm.hotspot.runtime.VM; +import sun.jvm.hotspot.runtime.JavaThread; +import sun.jvm.hotspot.memory.SystemDictionary; +import sun.jvm.hotspot.memory.SymbolTable; +import sun.jvm.hotspot.memory.Universe; +import sun.jvm.hotspot.utilities.Assert; + +import java.util.List; +import java.util.ArrayList; +import java.util.Map; +import java.util.Iterator; +import java.util.Collections; +import java.util.HashMap; +import java.util.Observer; +import java.util.StringTokenizer; +import java.lang.ref.SoftReference; +import java.lang.ref.ReferenceQueue; +import java.lang.ref.Reference; + +public class VirtualMachineImpl extends MirrorImpl implements PathSearchingVirtualMachine { + + private HotSpotAgent saAgent = new HotSpotAgent(); + private VM saVM; + private Universe saUniverse; + private SystemDictionary saSystemDictionary; + private SymbolTable saSymbolTable; + private ObjectHeap saObjectHeap; + + VM saVM() { + return saVM; + } + + SystemDictionary saSystemDictionary() { + return saSystemDictionary; + } + + SymbolTable saSymbolTable() { + return saSymbolTable; + } + + Universe saUniverse() { + return saUniverse; + } + + ObjectHeap saObjectHeap() { + return saObjectHeap; + } + + com.sun.jdi.VirtualMachineManager vmmgr; + + private final ThreadGroup threadGroupForJDI; + + // Per-vm singletons for primitive types and for void. + // singleton-ness protected by "synchronized(this)". + private BooleanType theBooleanType; + private ByteType theByteType; + private CharType theCharType; + private ShortType theShortType; + private IntegerType theIntegerType; + private LongType theLongType; + private FloatType theFloatType; + private DoubleType theDoubleType; + + private VoidType theVoidType; + + private VoidValue voidVal; + private Map typesByID; // Map<Klass, ReferenceTypeImpl> + private List typesBySignature; // List<ReferenceTypeImpl> - used in signature search + private boolean retrievedAllTypes = false; + private List bootstrapClasses; // all bootstrap classes + private ArrayList allThreads; + private ArrayList topLevelGroups; + final int sequenceNumber; + + // ObjectReference cache + // "objectsByID" protected by "synchronized(this)". + private final Map objectsByID = new HashMap(); + private final ReferenceQueue referenceQueue = new ReferenceQueue(); + + // names of some well-known classes to jdi + private Symbol javaLangString; + private Symbol javaLangThread; + private Symbol javaLangThreadGroup; + private Symbol javaLangClass; + private Symbol javaLangClassLoader; + + // used in ReferenceTypeImpl.isThrowableBacktraceField + private Symbol javaLangThrowable; + + // names of classes used in array assignment check + // refer to ArrayTypeImpl.isAssignableTo + private Symbol javaLangObject; + private Symbol javaLangCloneable; + private Symbol javaIoSerializable; + + // symbol used in ClassTypeImpl.isEnum check + private Symbol javaLangEnum; + + Symbol javaLangObject() { + return javaLangObject; + } + + Symbol javaLangCloneable() { + return javaLangCloneable; + } + + Symbol javaIoSerializable() { + return javaIoSerializable; + } + + Symbol javaLangEnum() { + return javaLangEnum; + } + + Symbol javaLangThrowable() { + return javaLangThrowable; + } + + // name of the current default stratum + private String defaultStratum; + + // initialize known class name symbols + private void initClassNameSymbols() { + SymbolTable st = saSymbolTable(); + javaLangString = st.probe("java/lang/String"); + javaLangThread = st.probe("java/lang/Thread"); + javaLangThreadGroup = st.probe("java/lang/ThreadGroup"); + javaLangClass = st.probe("java/lang/Class"); + javaLangClassLoader = st.probe("java/lang/ClassLoader"); + javaLangThrowable = st.probe("java/lang/Throwable"); + javaLangObject = st.probe("java/lang/Object"); + javaLangCloneable = st.probe("java/lang/Cloneable"); + javaIoSerializable = st.probe("java/io/Serializable"); + javaLangEnum = st.probe("java/lang/Enum"); + } + + private void init() { + saVM = VM.getVM(); + saUniverse = saVM.getUniverse(); + saSystemDictionary = saVM.getSystemDictionary(); + saSymbolTable = saVM.getSymbolTable(); + saObjectHeap = saVM.getObjectHeap(); + initClassNameSymbols(); + } + + static public VirtualMachineImpl createVirtualMachineForCorefile(VirtualMachineManager mgr, + String javaExecutableName, + String coreFileName, + int sequenceNumber) + throws Exception { + if (Assert.ASSERTS_ENABLED) { + Assert.that(coreFileName != null, "SA VirtualMachineImpl: core filename = null is not yet implemented"); + } + if (Assert.ASSERTS_ENABLED) { + Assert.that(javaExecutableName != null, "SA VirtualMachineImpl: java executable = null is not yet implemented"); + } + + VirtualMachineImpl myvm = new VirtualMachineImpl(mgr, sequenceNumber); + try { + myvm.saAgent.attach(javaExecutableName, coreFileName); + myvm.init(); + } catch (Exception ee) { + myvm.saAgent.detach(); + throw ee; + } + return myvm; + } + + static public VirtualMachineImpl createVirtualMachineForPID(VirtualMachineManager mgr, + int pid, + int sequenceNumber) + throws Exception { + + VirtualMachineImpl myvm = new VirtualMachineImpl(mgr, sequenceNumber); + try { + myvm.saAgent.attach(pid); + myvm.init(); + } catch (Exception ee) { + myvm.saAgent.detach(); + throw ee; + } + return myvm; + } + + static public VirtualMachineImpl createVirtualMachineForServer(VirtualMachineManager mgr, + String server, + int sequenceNumber) + throws Exception { + if (Assert.ASSERTS_ENABLED) { + Assert.that(server != null, "SA VirtualMachineImpl: DebugServer = null is not yet implemented"); + } + + VirtualMachineImpl myvm = new VirtualMachineImpl(mgr, sequenceNumber); + try { + myvm.saAgent.attach(server); + myvm.init(); + } catch (Exception ee) { + myvm.saAgent.detach(); + throw ee; + } + return myvm; + } + + + VirtualMachineImpl(VirtualMachineManager mgr, int sequenceNumber) + throws Exception { + super(null); // Can't use super(this) + vm = this; + + this.sequenceNumber = sequenceNumber; + this.vmmgr = mgr; + + /* Create ThreadGroup to be used by all threads servicing + * this VM. + */ + threadGroupForJDI = new ThreadGroup("JDI [" + + this.hashCode() + "]"); + + ((com.sun.tools.jdi.VirtualMachineManagerImpl)mgr).addVirtualMachine(this); + + // By default SA agent classes prefer dbx debugger to proc debugger + // and Windows process debugger to windbg debugger. SA expects + // special properties to be set to choose other debuggers. We will set + // those here before attaching to SA agent. + + System.setProperty("sun.jvm.hotspot.debugger.useProcDebugger", "true"); + System.setProperty("sun.jvm.hotspot.debugger.useWindbgDebugger", "true"); + } + + // we reflectively use newly spec'ed class because our ALT_BOOTDIR + // is 1.4.2 and not 1.5. + private static Class vmCannotBeModifiedExceptionClass = null; + void throwNotReadOnlyException(String operation) { + RuntimeException re = null; + if (vmCannotBeModifiedExceptionClass == null) { + try { + vmCannotBeModifiedExceptionClass = Class.forName("com.sun.jdi.VMCannotBeModifiedException"); + } catch (ClassNotFoundException cnfe) { + vmCannotBeModifiedExceptionClass = UnsupportedOperationException.class; + } + } + try { + re = (RuntimeException) vmCannotBeModifiedExceptionClass.newInstance(); + } catch (Exception exp) { + re = new RuntimeException(exp.getMessage()); + } + throw re; + } + + public boolean equals(Object obj) { + // Oh boy; big recursion troubles if we don't have this! + // See MirrorImpl.equals + return this == obj; + } + + public int hashCode() { + // big recursion if we don't have this. See MirrorImpl.hashCode + return System.identityHashCode(this); + } + + public List classesByName(String className) { + String signature = JNITypeParser.typeNameToSignature(className); + List list; + if (!retrievedAllTypes) { + retrieveAllClasses(); + } + list = findReferenceTypes(signature); + return Collections.unmodifiableList(list); + } + + public List allClasses() { + if (!retrievedAllTypes) { + retrieveAllClasses(); + } + ArrayList a; + synchronized (this) { + a = new ArrayList(typesBySignature); + } + return Collections.unmodifiableList(a); + } + + // classes loaded by bootstrap loader + List bootstrapClasses() { + if (bootstrapClasses == null) { + bootstrapClasses = new ArrayList(); + List all = allClasses(); + for (Iterator itr = all.iterator(); itr.hasNext();) { + ReferenceType type = (ReferenceType) itr.next(); + if (type.classLoader() == null) { + bootstrapClasses.add(type); + } + } + } + return bootstrapClasses; + } + + private synchronized List findReferenceTypes(String signature) { + if (typesByID == null) { + return new ArrayList(0); + } + + // we haven't sorted types by signatures. But we can take + // advantage of comparing symbols instead of name. In the worst + // case, we will be comparing N addresses rather than N strings + // where N being total no. of classes in allClasses() list. + + // The signature could be Lx/y/z; or [.... + // If it is Lx/y/z; the internal type name is x/y/x + // for array klasses internal type name is same as + // signature + String typeName = null; + if (signature.charAt(0) == 'L') { + typeName = signature.substring(1, signature.length() - 1); + } else { + typeName = signature; + } + + Symbol typeNameSym = saSymbolTable().probe(typeName); + // if there is no symbol in VM, then we wouldn't have that type + if (typeNameSym == null) { + return new ArrayList(0); + } + + Iterator iter = typesBySignature.iterator(); + List list = new ArrayList(); + while (iter.hasNext()) { + // We have cached type name as symbol in reference type + ReferenceTypeImpl type = (ReferenceTypeImpl)iter.next(); + if (typeNameSym.equals(type.typeNameAsSymbol())) { + list.add(type); + } + } + return list; + } + + private void retrieveAllClasses() { + final List saKlasses = new ArrayList(); + SystemDictionary.ClassVisitor visitor = new SystemDictionary.ClassVisitor() { + public void visit(Klass k) { + for (Klass l = k; l != null; l = l.arrayKlassOrNull()) { + // for non-array classes filter out un-prepared classes + // refer to 'allClasses' in share/back/VirtualMachineImpl.c + if (l instanceof ArrayKlass) { + saKlasses.add(l); + } else { + int status = l.getClassStatus(); + if ((status & JVMDIClassStatus.PREPARED) != 0) { + saKlasses.add(l); + } + } + } + } + }; + + // refer to jvmtiGetLoadedClasses.cpp - getLoadedClasses in VM code. + + // classes from SystemDictionary + saSystemDictionary.classesDo(visitor); + + // From SystemDictionary we do not get primitive single + // dimensional array classes. add primitive single dimensional array + // klasses from Universe. + saVM.getUniverse().basicTypeClassesDo(visitor); + + // Hold lock during processing to improve performance + // and to have safe check/set of retrievedAllTypes + synchronized (this) { + if (!retrievedAllTypes) { + // Number of classes + int count = saKlasses.size(); + for (int ii = 0; ii < count; ii++) { + Klass kk = (Klass)saKlasses.get(ii); + ReferenceTypeImpl type = referenceType(kk); + } + retrievedAllTypes = true; + } + } + } + + ReferenceTypeImpl referenceType(Klass kk) { + ReferenceTypeImpl retType = null; + synchronized (this) { + if (typesByID != null) { + retType = (ReferenceTypeImpl)typesByID.get(kk); + } + if (retType == null) { + retType = addReferenceType(kk); + } + } + return retType; + } + + private void initReferenceTypes() { + typesByID = new HashMap(); + typesBySignature = new ArrayList(); + } + + private synchronized ReferenceTypeImpl addReferenceType(Klass kk) { + if (typesByID == null) { + initReferenceTypes(); + } + ReferenceTypeImpl newRefType = null; + if (kk instanceof ObjArrayKlass || kk instanceof TypeArrayKlass) { + newRefType = new ArrayTypeImpl(this, (ArrayKlass)kk); + } else if (kk instanceof InstanceKlass) { + if (kk.isInterface()) { + newRefType = new InterfaceTypeImpl(this, (InstanceKlass)kk); + } else { + newRefType = new ClassTypeImpl(this, (InstanceKlass)kk); + } + } else { + throw new RuntimeException("should not reach here"); + } + + typesByID.put(kk, newRefType); + typesBySignature.add(newRefType); + return newRefType; + } + + ThreadGroup threadGroupForJDI() { + return threadGroupForJDI; + } + + public void redefineClasses(Map classToBytes) { + throwNotReadOnlyException("VirtualMachineImpl.redefineClasses()"); + } + + private List getAllThreads() { + if (allThreads == null) { + allThreads = new ArrayList(10); // Might be enough, might not be + for (sun.jvm.hotspot.runtime.JavaThread thread = + saVM.getThreads().first(); thread != null; + thread = thread.next()) { + // refer to JvmtiEnv::GetAllThreads in jvmtiEnv.cpp. + // filter out the hidden-from-external-view threads. + if (thread.isHiddenFromExternalView() == false) { + ThreadReferenceImpl myThread = threadMirror(thread); + allThreads.add(myThread); + } + } + } + return allThreads; + } + + public List allThreads() { //fixme jjh + return Collections.unmodifiableList(getAllThreads()); + } + + public void suspend() { + throwNotReadOnlyException("VirtualMachineImpl.suspend()"); + } + + public void resume() { + throwNotReadOnlyException("VirtualMachineImpl.resume()"); + } + + public List topLevelThreadGroups() { //fixme jjh + // The doc for ThreadGroup says that The top-level thread group + // is the only thread group whose parent is null. This means there is + // only one top level thread group. There will be a thread in this + // group so we will just find a thread whose threadgroup has no parent + // and that will be it. + + if (topLevelGroups == null) { + topLevelGroups = new ArrayList(1); + Iterator myIt = getAllThreads().iterator(); + while (myIt.hasNext()) { + ThreadReferenceImpl myThread = (ThreadReferenceImpl)myIt.next(); + ThreadGroupReference myGroup = myThread.threadGroup(); + ThreadGroupReference myParent = myGroup.parent(); + if (myGroup.parent() == null) { + topLevelGroups.add(myGroup); + break; + } + } + } + return Collections.unmodifiableList(topLevelGroups); + } + + public EventQueue eventQueue() { + throwNotReadOnlyException("VirtualMachine.eventQueue()"); + return null; + } + + public EventRequestManager eventRequestManager() { + throwNotReadOnlyException("VirtualMachineImpl.eventRequestManager()"); + return null; + } + + public BooleanValue mirrorOf(boolean value) { + return new BooleanValueImpl(this,value); + } + + public ByteValue mirrorOf(byte value) { + return new ByteValueImpl(this,value); + } + + public CharValue mirrorOf(char value) { + return new CharValueImpl(this,value); + } + + public ShortValue mirrorOf(short value) { + return new ShortValueImpl(this,value); + } + + public IntegerValue mirrorOf(int value) { + return new IntegerValueImpl(this,value); + } + + public LongValue mirrorOf(long value) { + return new LongValueImpl(this,value); + } + + public FloatValue mirrorOf(float value) { + return new FloatValueImpl(this,value); + } + + public DoubleValue mirrorOf(double value) { + return new DoubleValueImpl(this,value); + } + + public StringReference mirrorOf(String value) { + throwNotReadOnlyException("VirtualMachinestop.mirrorOf(String)"); + return null; + } + + public VoidValue mirrorOfVoid() { + if (voidVal == null) { + voidVal = new VoidValueImpl(this); + } + return voidVal; + } + + + public Process process() { + throwNotReadOnlyException("VirtualMachine.process"); + return null; + } + + // dispose observer for Class re-use. refer to ConnectorImpl. + private Observer disposeObserver; + + // ConnectorImpl loaded by a different class loader can not access it. + // i.e., runtime package of <ConnectorImpl, L1> is not the same that of + // <VirtualMachineImpl, L2> when L1 != L2. So, package private method + // can be called reflectively after using setAccessible(true). + + void setDisposeObserver(Observer observer) { + disposeObserver = observer; + } + + private void notifyDispose() { + if (Assert.ASSERTS_ENABLED) { + Assert.that(disposeObserver != null, "null VM.dispose observer"); + } + disposeObserver.update(null, null); + } + + public void dispose() { + saAgent.detach(); + notifyDispose(); + } + + public void exit(int exitCode) { + throwNotReadOnlyException("VirtualMachine.exit(int)"); + } + + public boolean canBeModified() { + return false; + } + + public boolean canWatchFieldModification() { + return false; + } + + public boolean canWatchFieldAccess() { + return false; + } + + public boolean canGetBytecodes() { + return true; + } + + public boolean canGetSyntheticAttribute() { + return true; + } + + // FIXME: For now, all monitor capabilities are disabled + public boolean canGetOwnedMonitorInfo() { + return false; + } + + public boolean canGetCurrentContendedMonitor() { + return false; + } + + public boolean canGetMonitorInfo() { + return false; + } + + // because this SA works only with 1.5 and update releases + // this should always succeed unlike JVMDI/JDI. + public boolean canGet1_5LanguageFeatures() { + return true; + } + + public boolean canUseInstanceFilters() { + return false; + } + + public boolean canRedefineClasses() { + return false; + } + + public boolean canAddMethod() { + return false; + } + + public boolean canUnrestrictedlyRedefineClasses() { + return false; + } + + public boolean canPopFrames() { + return false; + } + + public boolean canGetSourceDebugExtension() { + // We can use InstanceKlass.getSourceDebugExtension only if + // ClassFileParser parsed the info. But, ClassFileParser parses + // SourceDebugExtension attribute only if corresponding JVMDI/TI + // capability is set to true. Currently, vmStructs does not expose + // JVMDI/TI capabilities and hence we conservatively assume false. + return false; + } + + public boolean canRequestVMDeathEvent() { + return false; + } + + // new method since 1.6 + public boolean canForceEarlyReturn() { + return false; + } + + // new method since 1.6 + public boolean canGetConstantPool() { + return true; + } + + // new method since 1.6 + public boolean canGetClassFileVersion() { + return true; + } + + // new method since 1.6. + public boolean canGetMethodReturnValues() { + return false; + } + + // new method since 1.6 + // Real body will be supplied later. + public boolean canGetInstanceInfo() { + return true; + } + + // new method since 1.6 + public boolean canUseSourceNameFilters() { + return false; + } + + // new method since 1.6. + public boolean canRequestMonitorEvents() { + return false; + } + + // new method since 1.6. + public boolean canGetMonitorFrameInfo() { + return true; + } + + // new method since 1.6 + // Real body will be supplied later. + public long[] instanceCounts(List classes) { + if (!canGetInstanceInfo()) { + throw new UnsupportedOperationException( + "target does not support getting instances"); + } + + final long[] retValue = new long[classes.size()] ; + + final Klass [] klassArray = new Klass[classes.size()]; + + boolean allAbstractClasses = true; + for (int i=0; i < classes.size(); i++) { + ReferenceTypeImpl rti = (ReferenceTypeImpl)classes.get(i); + klassArray[i] = rti.ref(); + retValue[i]=0; + if (!(rti.isAbstract() || ((ReferenceType)rti instanceof InterfaceType))) { + allAbstractClasses = false; + } + } + + if (allAbstractClasses) { + return retValue; + } + final int size = classes.size(); + saObjectHeap.iterate(new DefaultHeapVisitor() { + public boolean doObj(Oop oop) { + for (int i=0; i < size; i++) { + if (klassArray[i].equals(oop.getKlass())) { + retValue[i]++; + break; + } + } + return false; + } + }); + + return retValue; + } + + private List getPath (String pathName) { + String cp = saVM.getSystemProperty(pathName); + String pathSep = saVM.getSystemProperty("path.separator"); + ArrayList al = new ArrayList(); + StringTokenizer st = new StringTokenizer(cp, pathSep); + while (st.hasMoreTokens()) { + al.add(st.nextToken()); + } + al.trimToSize(); + return al; + } + + public List classPath() { + return getPath("java.class.path"); + } + + public List bootClassPath() { + return getPath("sun.boot.class.path"); + } + + public String baseDirectory() { + return saVM.getSystemProperty("user.dir"); + } + + public void setDefaultStratum(String stratum) { + defaultStratum = stratum; + } + + public String getDefaultStratum() { + return defaultStratum; + } + + public String description() { + String[] versionParts = {"" + vmmgr.majorInterfaceVersion(), + "" + vmmgr.minorInterfaceVersion(), + name()}; + return java.text.MessageFormat.format(java.util.ResourceBundle. + getBundle("com.sun.tools.jdi.resources.jdi").getString("version_format"), + versionParts); + } + + public String version() { + return saVM.getSystemProperty("java.version"); + } + + public String name() { + StringBuffer sb = new StringBuffer(); + sb.append("JVM version "); + sb.append(version()); + sb.append(" ("); + sb.append(saVM.getSystemProperty("java.vm.name")); + sb.append(", "); + sb.append(saVM.getSystemProperty("java.vm.info")); + sb.append(")"); + return sb.toString(); + } + + // from interface Mirror + public VirtualMachine virtualMachine() { + return this; + } + + public String toString() { + return name(); + } + + public void setDebugTraceMode(int traceFlags) { + // spec. says output is implementation dependent + // and trace mode may be ignored. we ignore it :-) + } + + // heap walking API + + // capability check + public boolean canWalkHeap() { + return true; + } + + // return a list of all objects in heap + public List/*<ObjectReference>*/ allObjects() { + final List objects = new ArrayList(0); + saObjectHeap.iterate( + new DefaultHeapVisitor() { + public boolean doObj(Oop oop) { + objects.add(objectMirror(oop)); + return false; + } + }); + return objects; + } + + // equivalent to objectsByType(type, true) + public List/*<ObjectReference>*/ objectsByType(ReferenceType type) { + return objectsByType(type, true); + } + + // returns objects of type exactly equal to given type + private List/*<ObjectReference>*/ objectsByExactType(ReferenceType type) { + final List objects = new ArrayList(0); + final Klass givenKls = ((ReferenceTypeImpl)type).ref(); + saObjectHeap.iterate(new DefaultHeapVisitor() { + public boolean doObj(Oop oop) { + if (givenKls.equals(oop.getKlass())) { + objects.add(objectMirror(oop)); + } + return false; + } + }); + return objects; + } + + // returns objects of given type as well as it's subtypes + private List/*<ObjectReference>*/ objectsBySubType(ReferenceType type) { + final List objects = new ArrayList(0); + final ReferenceType givenType = type; + saObjectHeap.iterate(new DefaultHeapVisitor() { + public boolean doObj(Oop oop) { + ReferenceTypeImpl curType = (ReferenceTypeImpl) referenceType(oop.getKlass()); + if (curType.isAssignableTo(givenType)) { + objects.add(objectMirror(oop)); + } + return false; + } + }); + return objects; + } + + // includeSubtypes - do you want to include subclass/subtype instances of given + // ReferenceType or do we want objects of exact type only? + public List/*<ObjectReference>*/ objectsByType(ReferenceType type, boolean includeSubtypes) { + Klass kls = ((ReferenceTypeImpl)type).ref(); + if (kls instanceof InstanceKlass) { + InstanceKlass ik = (InstanceKlass) kls; + if (ik.isInterface()) { + if (ik.nofImplementors() == 0L) { + return new ArrayList(0); + } + } else { + // if the Klass is final or if there are no subklasses loaded yet + if (ik.getAccessFlagsObj().isFinal() || ik.getSubklassKlass() == null) { + includeSubtypes = false; + } + } + } else { + // no subtypes for primitive array types + ArrayTypeImpl arrayType = (ArrayTypeImpl) type; + try { + Type componentType = arrayType.componentType(); + if (componentType instanceof PrimitiveType) { + includeSubtypes = false; + } + } catch (ClassNotLoadedException cnle) { + // ignore. component type not yet loaded + } + } + + if (includeSubtypes) { + return objectsBySubType(type); + } else { + return objectsByExactType(type); + } + } + + Type findBootType(String signature) throws ClassNotLoadedException { + List types = allClasses(); + Iterator iter = types.iterator(); + while (iter.hasNext()) { + ReferenceType type = (ReferenceType)iter.next(); + if ((type.classLoader() == null) && + (type.signature().equals(signature))) { + return type; + } + } + JNITypeParser parser = new JNITypeParser(signature); + throw new ClassNotLoadedException(parser.typeName(), + "Type " + parser.typeName() + " not loaded"); + } + + BooleanType theBooleanType() { + if (theBooleanType == null) { + synchronized(this) { + if (theBooleanType == null) { + theBooleanType = new BooleanTypeImpl(this); + } + } + } + return theBooleanType; + } + + ByteType theByteType() { + if (theByteType == null) { + synchronized(this) { + if (theByteType == null) { + theByteType = new ByteTypeImpl(this); + } + } + } + return theByteType; + } + + CharType theCharType() { + if (theCharType == null) { + synchronized(this) { + if (theCharType == null) { + theCharType = new CharTypeImpl(this); + } + } + } + return theCharType; + } + + ShortType theShortType() { + if (theShortType == null) { + synchronized(this) { + if (theShortType == null) { + theShortType = new ShortTypeImpl(this); + } + } + } + return theShortType; + } + + IntegerType theIntegerType() { + if (theIntegerType == null) { + synchronized(this) { + if (theIntegerType == null) { + theIntegerType = new IntegerTypeImpl(this); + } + } + } + return theIntegerType; + } + + LongType theLongType() { + if (theLongType == null) { + synchronized(this) { + if (theLongType == null) { + theLongType = new LongTypeImpl(this); + } + } + } + return theLongType; + } + + FloatType theFloatType() { + if (theFloatType == null) { + synchronized(this) { + if (theFloatType == null) { + theFloatType = new FloatTypeImpl(this); + } + } + } + return theFloatType; + } + + DoubleType theDoubleType() { + if (theDoubleType == null) { + synchronized(this) { + if (theDoubleType == null) { + theDoubleType = new DoubleTypeImpl(this); + } + } + } + return theDoubleType; + } + + VoidType theVoidType() { + if (theVoidType == null) { + synchronized(this) { + if (theVoidType == null) { + theVoidType = new VoidTypeImpl(this); + } + } + } + return theVoidType; + } + + PrimitiveType primitiveTypeMirror(char tag) { + switch (tag) { + case 'Z': + return theBooleanType(); + case 'B': + return theByteType(); + case 'C': + return theCharType(); + case 'S': + return theShortType(); + case 'I': + return theIntegerType(); + case 'J': + return theLongType(); + case 'F': + return theFloatType(); + case 'D': + return theDoubleType(); + default: + throw new IllegalArgumentException("Unrecognized primitive tag " + tag); + } + } + + private void processQueue() { + Reference ref; + while ((ref = referenceQueue.poll()) != null) { + SoftObjectReference softRef = (SoftObjectReference)ref; + removeObjectMirror(softRef); + } + } + + // Address value is used as uniqueID by ObjectReferenceImpl + long getAddressValue(Oop obj) { + return vm.saVM.getDebugger().getAddressValue(obj.getHandle()); + } + + synchronized ObjectReferenceImpl objectMirror(Oop key) { + + // Handle any queue elements that are not strongly reachable + processQueue(); + + if (key == null) { + return null; + } + ObjectReferenceImpl object = null; + + /* + * Attempt to retrieve an existing object object reference + */ + SoftObjectReference ref = (SoftObjectReference)objectsByID.get(key); + if (ref != null) { + object = ref.object(); + } + + /* + * If the object wasn't in the table, or it's soft reference was + * cleared, create a new instance. + */ + if (object == null) { + if (key instanceof Instance) { + // look for well-known classes + Symbol className = key.getKlass().getName(); + if (Assert.ASSERTS_ENABLED) { + Assert.that(className != null, "Null class name"); + } + Instance inst = (Instance) key; + if (className.equals(javaLangString)) { + object = new StringReferenceImpl(this, inst); + } else if (className.equals(javaLangThread)) { + object = new ThreadReferenceImpl(this, inst); + } else if (className.equals(javaLangThreadGroup)) { + object = new ThreadGroupReferenceImpl(this, inst); + } else if (className.equals(javaLangClass)) { + object = new ClassObjectReferenceImpl(this, inst); + } else if (className.equals(javaLangClassLoader)) { + object = new ClassLoaderReferenceImpl(this, inst); + } else { + // not a well-known class. But the base class may be + // one of the known classes. + Klass kls = key.getKlass().getSuper(); + while (kls != null) { + className = kls.getName(); + // java.lang.Class and java.lang.String are final classes + if (className.equals(javaLangThread)) { + object = new ThreadReferenceImpl(this, inst); + break; + } else if(className.equals(javaLangThreadGroup)) { + object = new ThreadGroupReferenceImpl(this, inst); + break; + } else if (className.equals(javaLangClassLoader)) { + object = new ClassLoaderReferenceImpl(this, inst); + break; + } + kls = kls.getSuper(); + } + + if (object == null) { + // create generic object reference + object = new ObjectReferenceImpl(this, inst); + } + } + } else if (key instanceof TypeArray) { + object = new ArrayReferenceImpl(this, (Array) key); + } else if (key instanceof ObjArray) { + object = new ArrayReferenceImpl(this, (Array) key); + } else { + throw new RuntimeException("unexpected object type " + key); + } + ref = new SoftObjectReference(key, object, referenceQueue); + + /* + * If there was no previous entry in the table, we add one here + * If the previous entry was cleared, we replace it here. + */ + objectsByID.put(key, ref); + } else { + ref.incrementCount(); + } + + return object; + } + + synchronized void removeObjectMirror(SoftObjectReference ref) { + /* + * This will remove the soft reference if it has not been + * replaced in the cache. + */ + objectsByID.remove(ref.key()); + } + + StringReferenceImpl stringMirror(Instance id) { + return (StringReferenceImpl) objectMirror(id); + } + + ArrayReferenceImpl arrayMirror(Array id) { + return (ArrayReferenceImpl) objectMirror(id); + } + + ThreadReferenceImpl threadMirror(Instance id) { + return (ThreadReferenceImpl) objectMirror(id); + } + + ThreadReferenceImpl threadMirror(JavaThread jt) { + return (ThreadReferenceImpl) objectMirror(jt.getThreadObj()); + } + + ThreadGroupReferenceImpl threadGroupMirror(Instance id) { + return (ThreadGroupReferenceImpl) objectMirror(id); + } + + ClassLoaderReferenceImpl classLoaderMirror(Instance id) { + return (ClassLoaderReferenceImpl) objectMirror(id); + } + + ClassObjectReferenceImpl classObjectMirror(Instance id) { + return (ClassObjectReferenceImpl) objectMirror(id); + } + + // Use of soft refs and caching stuff here has to be re-examined. + // It might not make sense for JDI - SA. + static private class SoftObjectReference extends SoftReference { + int count; + Object key; + + SoftObjectReference(Object key, ObjectReferenceImpl mirror, + ReferenceQueue queue) { + super(mirror, queue); + this.count = 1; + this.key = key; + } + + int count() { + return count; + } + + void incrementCount() { + count++; + } + + Object key() { + return key; + } + + ObjectReferenceImpl object() { + return (ObjectReferenceImpl)get(); + } + } +}