blob: c2b13f1636301a996726effaa76cd6d2d81ec861 [file] [log] [blame]
/*
* Copyright (c) 2002, 2009, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
package sun.jvm.hotspot.jdi;
import java.io.*;
import com.sun.jdi.*;
import sun.jvm.hotspot.debugger.Address;
import sun.jvm.hotspot.debugger.OopHandle;
import sun.jvm.hotspot.oops.Oop;
import sun.jvm.hotspot.oops.Mark;
import sun.jvm.hotspot.oops.Instance;
import sun.jvm.hotspot.oops.Array;
import sun.jvm.hotspot.oops.OopUtilities;
import sun.jvm.hotspot.oops.Klass;
import sun.jvm.hotspot.oops.DefaultHeapVisitor;
import sun.jvm.hotspot.runtime.JavaThread;
import sun.jvm.hotspot.runtime.JavaVFrame;
import sun.jvm.hotspot.runtime.MonitorInfo;
import sun.jvm.hotspot.runtime.ObjectMonitor;
import sun.jvm.hotspot.runtime.Threads;
import sun.jvm.hotspot.utilities.Assert;
import java.util.*;
public class ObjectReferenceImpl extends ValueImpl implements ObjectReference {
private Oop saObject;
private long myID;
private boolean monitorInfoCached = false;
private ThreadReferenceImpl owningThread = null;
private List waitingThreads = null; // List<ThreadReferenceImpl>
private int entryCount = 0;
private static long nextID = 0L;
private static synchronized long nextID() {
return nextID++;
}
ObjectReferenceImpl(VirtualMachine aVm, sun.jvm.hotspot.oops.Oop oRef) {
super(aVm);
saObject = oRef;
myID = nextID();
}
protected Oop ref() {
return saObject;
}
public Type type() {
return referenceType();
}
public ReferenceType referenceType() {
Klass myKlass = ref().getKlass();
return vm.referenceType(myKlass);
}
public Value getValue(Field sig) {
List list = new ArrayList(1);
list.add(sig);
Map map = getValues(list);
return(Value)map.get(sig);
}
public Map getValues(List theFields) {
//validateMirrors(theFields);
List staticFields = new ArrayList(0);
int size = theFields.size();
List instanceFields = new ArrayList(size);
for (int i=0; i<size; i++) {
sun.jvm.hotspot.jdi.FieldImpl field =
(sun.jvm.hotspot.jdi.FieldImpl)theFields.get(i);
// Make sure the field is valid
((ReferenceTypeImpl)referenceType()).validateFieldAccess(field);
// FIX ME! We need to do some sanity checking
// here; make sure the field belongs to this
// object.
if (field.isStatic()) {
staticFields.add(field);
} else {
instanceFields.add(field);
}
}
// Look up static field(s) first to mimic the JDI implementation
Map map;
if (staticFields.size() > 0) {
map = referenceType().getValues(staticFields);
} else {
map = new HashMap(size);
}
// Then get instance field(s)
size = instanceFields.size();
for (int ii=0; ii<size; ii++){
FieldImpl fieldImpl = (FieldImpl)instanceFields.get(ii);
map.put(fieldImpl, fieldImpl.getValue(saObject));
}
return map;
}
public void setValue(Field field, Value value)
throws InvalidTypeException, ClassNotLoadedException {
vm.throwNotReadOnlyException("ObjectReference.setValue(...)");
}
public Value invokeMethod(ThreadReference threadIntf, Method methodIntf,
List arguments, int options)
throws InvalidTypeException,
IncompatibleThreadStateException,
InvocationException,
ClassNotLoadedException {
vm.throwNotReadOnlyException("ObjectReference.invokeMethod(...)");
return null;
}
public void disableCollection() {
vm.throwNotReadOnlyException("ObjectReference.disableCollection()");
}
public void enableCollection() {
vm.throwNotReadOnlyException("ObjectReference.enableCollection()");
}
public boolean isCollected() {
vm.throwNotReadOnlyException("ObjectReference.isCollected()");
return false;
}
public long uniqueID() {
return myID;
}
public List waitingThreads() throws IncompatibleThreadStateException {
if (vm.canGetMonitorInfo() == false) {
throw new UnsupportedOperationException();
}
if (! monitorInfoCached) {
computeMonitorInfo();
}
return waitingThreads;
}
public ThreadReference owningThread() throws IncompatibleThreadStateException {
if (vm.canGetMonitorInfo() == false) {
throw new UnsupportedOperationException();
}
if (! monitorInfoCached) {
computeMonitorInfo();
}
return owningThread;
}
public int entryCount() throws IncompatibleThreadStateException {
if (vm.canGetMonitorInfo() == false) {
throw new UnsupportedOperationException();
}
if (! monitorInfoCached) {
computeMonitorInfo();
}
return entryCount;
}
// new method since 1.6.
// Real body will be supplied later.
public List referringObjects(long maxReferrers) {
if (!vm.canGetInstanceInfo()) {
throw new UnsupportedOperationException(
"target does not support getting instances");
}
if (maxReferrers < 0) {
throw new IllegalArgumentException("maxReferrers is less than zero: "
+ maxReferrers);
}
final ObjectReference obj = this;
final List objects = new ArrayList(0);
final long max = maxReferrers;
vm.saObjectHeap().iterate(new DefaultHeapVisitor() {
private long refCount = 0;
public boolean doObj(Oop oop) {
try {
ObjectReference objref = vm.objectMirror(oop);
List fields = objref.referenceType().allFields();
for (int i=0; i < fields.size(); i++) {
Field fld = (Field)fields.get(i);
if (objref.getValue(fld).equals(obj) && !objects.contains(objref)) {
objects.add(objref);
refCount++;
}
}
if (max > 0 && refCount >= max) {
return true;
}
} catch (RuntimeException x) {
// Ignore RuntimeException thrown from vm.objectMirror(oop)
// for bad oop. It is possible to see some bad oop
// because heap might be iterating at no safepoint.
}
return false;
}
});
return objects;
}
// refer to JvmtiEnvBase::count_locked_objects.
// Count the number of objects for a lightweight monitor. The obj
// parameter is object that owns the monitor so this routine will
// count the number of times the same object was locked by frames
// in JavaThread. i.e., we count total number of times the same
// object is (lightweight) locked by given thread.
private int countLockedObjects(JavaThread jt, Oop obj) {
int res = 0;
JavaVFrame frame = jt.getLastJavaVFrameDbg();
while (frame != null) {
List monitors = frame.getMonitors();
OopHandle givenHandle = obj.getHandle();
for (Iterator itr = monitors.iterator(); itr.hasNext();) {
MonitorInfo mi = (MonitorInfo) itr.next();
if (mi.eliminated() && frame.isCompiledFrame()) continue; // skip eliminated monitor
if (givenHandle.equals(mi.owner())) {
res++;
}
}
frame = (JavaVFrame) frame.javaSender();
}
return res;
}
// wrappers on same named method of Threads class
// returns List<JavaThread>
private List getPendingThreads(ObjectMonitor mon) {
return vm.saVM().getThreads().getPendingThreads(mon);
}
// returns List<JavaThread>
private List getWaitingThreads(ObjectMonitor mon) {
return vm.saVM().getThreads().getWaitingThreads(mon);
}
private JavaThread owningThreadFromMonitor(Address addr) {
return vm.saVM().getThreads().owningThreadFromMonitor(addr);
}
// refer to JvmtiEnv::GetObjectMonitorUsage
private void computeMonitorInfo() {
monitorInfoCached = true;
Mark mark = saObject.getMark();
ObjectMonitor mon = null;
Address owner = null;
// check for heavyweight monitor
if (! mark.hasMonitor()) {
// check for lightweight monitor
if (mark.hasLocker()) {
owner = mark.locker().getAddress(); // save the address of the Lock word
}
// implied else: no owner
} else {
// this object has a heavyweight monitor
mon = mark.monitor();
// The owner field of a heavyweight monitor may be NULL for no
// owner, a JavaThread * or it may still be the address of the
// Lock word in a JavaThread's stack. A monitor can be inflated
// by a non-owning JavaThread, but only the owning JavaThread
// can change the owner field from the Lock word to the
// JavaThread * and it may not have done that yet.
owner = mon.owner();
}
// find the owning thread
if (owner != null) {
owningThread = vm.threadMirror(owningThreadFromMonitor(owner));
}
// compute entryCount
if (owningThread != null) {
if (owningThread.getJavaThread().getAddress().equals(owner)) {
// the owner field is the JavaThread *
if (Assert.ASSERTS_ENABLED) {
Assert.that(false, "must have heavyweight monitor with JavaThread * owner");
}
entryCount = (int) mark.monitor().recursions() + 1;
} else {
// The owner field is the Lock word on the JavaThread's stack
// so the recursions field is not valid. We have to count the
// number of recursive monitor entries the hard way.
entryCount = countLockedObjects(owningThread.getJavaThread(), saObject);
}
}
// find the contenders & waiters
waitingThreads = new ArrayList();
if (mon != null) {
// this object has a heavyweight monitor. threads could
// be contenders or waiters
// add all contenders
List pendingThreads = getPendingThreads(mon);
// convert the JavaThreads to ThreadReferenceImpls
for (Iterator itrPend = pendingThreads.iterator(); itrPend.hasNext();) {
waitingThreads.add(vm.threadMirror((JavaThread) itrPend.next()));
}
// add all waiters (threads in Object.wait())
// note that we don't do this JVMTI way. To do it JVMTI way,
// we would need to access ObjectWaiter list maintained in
// ObjectMonitor::_queue. But we don't have this struct exposed
// in vmStructs. We do waiters list in a way similar to getting
// pending threads list
List objWaitingThreads = getWaitingThreads(mon);
// convert the JavaThreads to ThreadReferenceImpls
for (Iterator itrWait = objWaitingThreads.iterator(); itrWait.hasNext();) {
waitingThreads.add(vm.threadMirror((JavaThread) itrWait.next()));
}
}
}
public boolean equals(Object obj) {
if ((obj != null) && (obj instanceof ObjectReferenceImpl)) {
ObjectReferenceImpl other = (ObjectReferenceImpl)obj;
return (ref().equals(other.ref())) &&
super.equals(obj);
} else {
return false;
}
}
public int hashCode() {
return saObject.hashCode();
}
public String toString() {
return "instance of " + referenceType().name() + "(id=" + uniqueID() + ")";
}
}