6700889: Thread resume invalidates all stack frames, even from other threads
6701700: MonitorInfo objects aren't invalidated when the owning thread is resumed Don't inform ThreadListeners for thread T1 when some other thread is resumed, and MonitoryIfoImpl must add itself as a ThreadListener Reviewed-by: dcubed
This commit is contained in:
parent
5967d518b5
commit
4c95421013
@ -40,11 +40,12 @@ public class MonitorInfoImpl extends MirrorImpl
|
|||||||
int stack_depth;
|
int stack_depth;
|
||||||
|
|
||||||
MonitorInfoImpl(VirtualMachine vm, ObjectReference mon,
|
MonitorInfoImpl(VirtualMachine vm, ObjectReference mon,
|
||||||
ThreadReference thread, int dpth) {
|
ThreadReferenceImpl thread, int dpth) {
|
||||||
super(vm);
|
super(vm);
|
||||||
this.monitor = mon;
|
this.monitor = mon;
|
||||||
this.thread = thread;
|
this.thread = thread;
|
||||||
this.stack_depth = dpth;
|
this.stack_depth = dpth;
|
||||||
|
thread.addListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -35,12 +35,34 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl
|
|||||||
static final int SUSPEND_STATUS_SUSPENDED = 0x1;
|
static final int SUSPEND_STATUS_SUSPENDED = 0x1;
|
||||||
static final int SUSPEND_STATUS_BREAK = 0x2;
|
static final int SUSPEND_STATUS_BREAK = 0x2;
|
||||||
|
|
||||||
private ThreadGroupReference threadGroup;
|
|
||||||
private int suspendedZombieCount = 0;
|
private int suspendedZombieCount = 0;
|
||||||
|
|
||||||
// This is cached only while the VM is suspended
|
/*
|
||||||
private static class Cache extends ObjectReferenceImpl.Cache {
|
* Some objects can only be created while a thread is suspended and are valid
|
||||||
String name = null;
|
* only while the thread remains suspended. Examples are StackFrameImpl
|
||||||
|
* and MonitorInfoImpl. When the thread resumes, these objects have to be
|
||||||
|
* marked as invalid so that their methods can throw
|
||||||
|
* InvalidStackFrameException if they are called. To do this, such objects
|
||||||
|
* register themselves as listeners of the associated thread. When the
|
||||||
|
* thread is resumed, its listeners are notified and mark themselves
|
||||||
|
* invalid.
|
||||||
|
* Also, note that ThreadReferenceImpl itself caches some info that
|
||||||
|
* is valid only as long as the thread is suspended. When the thread
|
||||||
|
* is resumed, that cache must be purged.
|
||||||
|
* Lastly, note that ThreadReferenceImpl and its super, ObjectReferenceImpl
|
||||||
|
* cache some info that is only valid as long as the entire VM is suspended.
|
||||||
|
* If _any_ thread is resumed, this cache must be purged. To handle this,
|
||||||
|
* both ThreadReferenceImpl and ObjectReferenceImpl register themselves as
|
||||||
|
* VMListeners so that they get notified when all threads are suspended and
|
||||||
|
* when any thread is resumed.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// This is cached for the life of the thread
|
||||||
|
private ThreadGroupReference threadGroup;
|
||||||
|
|
||||||
|
// This is cached only while this one thread is suspended. Each time
|
||||||
|
// the thread is resumed, we clear this and start with a fresh one.
|
||||||
|
private static class LocalCache {
|
||||||
JDWP.ThreadReference.Status status = null;
|
JDWP.ThreadReference.Status status = null;
|
||||||
List<StackFrame> frames = null;
|
List<StackFrame> frames = null;
|
||||||
int framesStart = -1;
|
int framesStart = -1;
|
||||||
@ -52,6 +74,17 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl
|
|||||||
boolean triedCurrentContended = false;
|
boolean triedCurrentContended = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private LocalCache localCache;
|
||||||
|
|
||||||
|
private void resetLocalCache() {
|
||||||
|
localCache = new LocalCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is cached only while all threads in the VM are suspended
|
||||||
|
// Yes, someone could change the name of a thread while it is suspended.
|
||||||
|
private static class Cache extends ObjectReferenceImpl.Cache {
|
||||||
|
String name = null;
|
||||||
|
}
|
||||||
protected ObjectReferenceImpl.Cache newCache() {
|
protected ObjectReferenceImpl.Cache newCache() {
|
||||||
return new Cache();
|
return new Cache();
|
||||||
}
|
}
|
||||||
@ -59,8 +92,10 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl
|
|||||||
// Listeners - synchronized on vm.state()
|
// Listeners - synchronized on vm.state()
|
||||||
private List<WeakReference<ThreadListener>> listeners = new ArrayList<WeakReference<ThreadListener>>();
|
private List<WeakReference<ThreadListener>> listeners = new ArrayList<WeakReference<ThreadListener>>();
|
||||||
|
|
||||||
|
|
||||||
ThreadReferenceImpl(VirtualMachine aVm, long aRef) {
|
ThreadReferenceImpl(VirtualMachine aVm, long aRef) {
|
||||||
super(aVm,aRef);
|
super(aVm,aRef);
|
||||||
|
resetLocalCache();
|
||||||
vm.state().addListener(this);
|
vm.state().addListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,10 +107,24 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl
|
|||||||
* VMListener implementation
|
* VMListener implementation
|
||||||
*/
|
*/
|
||||||
public boolean vmNotSuspended(VMAction action) {
|
public boolean vmNotSuspended(VMAction action) {
|
||||||
synchronized (vm.state()) {
|
if (action.resumingThread() == null) {
|
||||||
processThreadAction(new ThreadAction(this,
|
// all threads are being resumed
|
||||||
ThreadAction.THREAD_RESUMABLE));
|
synchronized (vm.state()) {
|
||||||
|
processThreadAction(new ThreadAction(this,
|
||||||
|
ThreadAction.THREAD_RESUMABLE));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Othewise, only one thread is being resumed:
|
||||||
|
* if it is us,
|
||||||
|
* we have already done our processThreadAction to notify our
|
||||||
|
* listeners when we processed the resume.
|
||||||
|
* if it is not us,
|
||||||
|
* we don't want to notify our listeners
|
||||||
|
* because we are not being resumed.
|
||||||
|
*/
|
||||||
return super.vmNotSuspended(action);
|
return super.vmNotSuspended(action);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -191,23 +240,19 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl
|
|||||||
}
|
}
|
||||||
|
|
||||||
private JDWP.ThreadReference.Status jdwpStatus() {
|
private JDWP.ThreadReference.Status jdwpStatus() {
|
||||||
JDWP.ThreadReference.Status status = null;
|
JDWP.ThreadReference.Status myStatus = localCache.status;
|
||||||
try {
|
try {
|
||||||
Cache local = (Cache)getCache();
|
if (myStatus == null) {
|
||||||
|
myStatus = JDWP.ThreadReference.Status.process(vm, this);
|
||||||
if (local != null) {
|
if ((myStatus.suspendStatus & SUSPEND_STATUS_SUSPENDED) != 0) {
|
||||||
status = local.status;
|
// thread is suspended, we can cache the status.
|
||||||
}
|
localCache.status = myStatus;
|
||||||
if (status == null) {
|
|
||||||
status = JDWP.ThreadReference.Status.process(vm, this);
|
|
||||||
if (local != null) {
|
|
||||||
local.status = status;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (JDWPException exc) {
|
} catch (JDWPException exc) {
|
||||||
throw exc.toJDIException();
|
throw exc.toJDIException();
|
||||||
}
|
}
|
||||||
return status;
|
return myStatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int status() {
|
public int status() {
|
||||||
@ -245,8 +290,7 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl
|
|||||||
|
|
||||||
public ThreadGroupReference threadGroup() {
|
public ThreadGroupReference threadGroup() {
|
||||||
/*
|
/*
|
||||||
* Thread group can't change, so it's cached more conventionally
|
* Thread group can't change, so it's cached once and for all.
|
||||||
* than other things in this class.
|
|
||||||
*/
|
*/
|
||||||
if (threadGroup == null) {
|
if (threadGroup == null) {
|
||||||
try {
|
try {
|
||||||
@ -260,19 +304,10 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl
|
|||||||
}
|
}
|
||||||
|
|
||||||
public int frameCount() throws IncompatibleThreadStateException {
|
public int frameCount() throws IncompatibleThreadStateException {
|
||||||
int frameCount = -1;
|
|
||||||
try {
|
try {
|
||||||
Cache local = (Cache)getCache();
|
if (localCache.frameCount == -1) {
|
||||||
|
localCache.frameCount = JDWP.ThreadReference.FrameCount
|
||||||
if (local != null) {
|
|
||||||
frameCount = local.frameCount;
|
|
||||||
}
|
|
||||||
if (frameCount == -1) {
|
|
||||||
frameCount = JDWP.ThreadReference.FrameCount
|
|
||||||
.process(vm, this).frameCount;
|
.process(vm, this).frameCount;
|
||||||
if (local != null) {
|
|
||||||
local.frameCount = frameCount;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch (JDWPException exc) {
|
} catch (JDWPException exc) {
|
||||||
switch (exc.errorCode()) {
|
switch (exc.errorCode()) {
|
||||||
@ -283,7 +318,7 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl
|
|||||||
throw exc.toJDIException();
|
throw exc.toJDIException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return frameCount;
|
return localCache.frameCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<StackFrame> frames() throws IncompatibleThreadStateException {
|
public List<StackFrame> frames() throws IncompatibleThreadStateException {
|
||||||
@ -297,23 +332,25 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Is the requested subrange within what has been retrieved?
|
* Is the requested subrange within what has been retrieved?
|
||||||
* local is known to be non-null
|
* local is known to be non-null. Should only be called from
|
||||||
|
* a sync method.
|
||||||
*/
|
*/
|
||||||
private boolean isSubrange(Cache local,
|
private boolean isSubrange(LocalCache localCache,
|
||||||
int start, int length, List frames) {
|
int start, int length) {
|
||||||
if (start < local.framesStart) {
|
if (start < localCache.framesStart) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (length == -1) {
|
if (length == -1) {
|
||||||
return (local.framesLength == -1);
|
return (localCache.framesLength == -1);
|
||||||
}
|
}
|
||||||
if (local.framesLength == -1) {
|
if (localCache.framesLength == -1) {
|
||||||
if ((start + length) > (local.framesStart + frames.size())) {
|
if ((start + length) > (localCache.framesStart +
|
||||||
|
localCache.frames.size())) {
|
||||||
throw new IndexOutOfBoundsException();
|
throw new IndexOutOfBoundsException();
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return ((start + length) <= (local.framesStart + local.framesLength));
|
return ((start + length) <= (localCache.framesStart + localCache.framesLength));
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<StackFrame> frames(int start, int length)
|
public List<StackFrame> frames(int start, int length)
|
||||||
@ -329,51 +366,42 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl
|
|||||||
* Private version of frames() allows "-1" to specify all
|
* Private version of frames() allows "-1" to specify all
|
||||||
* remaining frames.
|
* remaining frames.
|
||||||
*/
|
*/
|
||||||
private List<StackFrame> privateFrames(int start, int length)
|
synchronized private List<StackFrame> privateFrames(int start, int length)
|
||||||
throws IncompatibleThreadStateException {
|
throws IncompatibleThreadStateException {
|
||||||
List<StackFrame> frames = null;
|
|
||||||
try {
|
|
||||||
Cache local = (Cache)getCache();
|
|
||||||
|
|
||||||
if (local != null) {
|
// Lock must be held while creating stack frames so if that two threads
|
||||||
frames = local.frames;
|
// do this at the same time, one won't clobber the subset created by the other.
|
||||||
}
|
|
||||||
if (frames == null || !isSubrange(local, start, length, frames)) {
|
try {
|
||||||
|
if (localCache.frames == null || !isSubrange(localCache, start, length)) {
|
||||||
JDWP.ThreadReference.Frames.Frame[] jdwpFrames
|
JDWP.ThreadReference.Frames.Frame[] jdwpFrames
|
||||||
= JDWP.ThreadReference.Frames.
|
= JDWP.ThreadReference.Frames.
|
||||||
process(vm, this, start, length).frames;
|
process(vm, this, start, length).frames;
|
||||||
int count = jdwpFrames.length;
|
int count = jdwpFrames.length;
|
||||||
frames = new ArrayList<StackFrame>(count);
|
localCache.frames = new ArrayList<StackFrame>(count);
|
||||||
|
|
||||||
// Lock must be held while creating stack frames.
|
for (int i = 0; i<count; i++) {
|
||||||
// so that a resume will not resume a partially
|
if (jdwpFrames[i].location == null) {
|
||||||
// created stack.
|
throw new InternalException("Invalid frame location");
|
||||||
synchronized (vm.state()) {
|
|
||||||
for (int i = 0; i<count; i++) {
|
|
||||||
if (jdwpFrames[i].location == null) {
|
|
||||||
throw new InternalException("Invalid frame location");
|
|
||||||
}
|
|
||||||
StackFrame frame = new StackFrameImpl(vm, this,
|
|
||||||
jdwpFrames[i].frameID,
|
|
||||||
jdwpFrames[i].location);
|
|
||||||
// Add to the frame list
|
|
||||||
frames.add(frame);
|
|
||||||
}
|
}
|
||||||
|
StackFrame frame = new StackFrameImpl(vm, this,
|
||||||
|
jdwpFrames[i].frameID,
|
||||||
|
jdwpFrames[i].location);
|
||||||
|
// Add to the frame list
|
||||||
|
localCache.frames.add(frame);
|
||||||
}
|
}
|
||||||
if (local != null) {
|
localCache.framesStart = start;
|
||||||
local.frames = frames;
|
localCache.framesLength = length;
|
||||||
local.framesStart = start;
|
return Collections.unmodifiableList(localCache.frames);
|
||||||
local.framesLength = length;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
int fromIndex = start - local.framesStart;
|
int fromIndex = start - localCache.framesStart;
|
||||||
int toIndex;
|
int toIndex;
|
||||||
if (length == -1) {
|
if (length == -1) {
|
||||||
toIndex = frames.size() - fromIndex;
|
toIndex = localCache.frames.size() - fromIndex;
|
||||||
} else {
|
} else {
|
||||||
toIndex = fromIndex + length;
|
toIndex = fromIndex + length;
|
||||||
}
|
}
|
||||||
frames = frames.subList(fromIndex, toIndex);
|
return Collections.unmodifiableList(localCache.frames.subList(fromIndex, toIndex));
|
||||||
}
|
}
|
||||||
} catch (JDWPException exc) {
|
} catch (JDWPException exc) {
|
||||||
switch (exc.errorCode()) {
|
switch (exc.errorCode()) {
|
||||||
@ -384,28 +412,18 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl
|
|||||||
throw exc.toJDIException();
|
throw exc.toJDIException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Collections.unmodifiableList(frames);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<ObjectReference> ownedMonitors() throws IncompatibleThreadStateException {
|
public List<ObjectReference> ownedMonitors() throws IncompatibleThreadStateException {
|
||||||
List<ObjectReference> monitors = null;
|
|
||||||
try {
|
try {
|
||||||
Cache local = (Cache)getCache();
|
if (localCache.ownedMonitors == null) {
|
||||||
|
localCache.ownedMonitors = Arrays.asList(
|
||||||
if (local != null) {
|
|
||||||
monitors = local.ownedMonitors;
|
|
||||||
}
|
|
||||||
if (monitors == null) {
|
|
||||||
monitors = Arrays.asList(
|
|
||||||
(ObjectReference[])JDWP.ThreadReference.OwnedMonitors.
|
(ObjectReference[])JDWP.ThreadReference.OwnedMonitors.
|
||||||
process(vm, this).owned);
|
process(vm, this).owned);
|
||||||
if (local != null) {
|
if ((vm.traceFlags & vm.TRACE_OBJREFS) != 0) {
|
||||||
local.ownedMonitors = monitors;
|
vm.printTrace(description() +
|
||||||
if ((vm.traceFlags & vm.TRACE_OBJREFS) != 0) {
|
" temporarily caching owned monitors"+
|
||||||
vm.printTrace(description() +
|
" (count = " + localCache.ownedMonitors.size() + ")");
|
||||||
" temporarily caching owned monitors"+
|
|
||||||
" (count = " + monitors.size() + ")");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (JDWPException exc) {
|
} catch (JDWPException exc) {
|
||||||
@ -417,29 +435,22 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl
|
|||||||
throw exc.toJDIException();
|
throw exc.toJDIException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return monitors;
|
return localCache.ownedMonitors;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ObjectReference currentContendedMonitor()
|
public ObjectReference currentContendedMonitor()
|
||||||
throws IncompatibleThreadStateException {
|
throws IncompatibleThreadStateException {
|
||||||
ObjectReference monitor = null;
|
|
||||||
try {
|
try {
|
||||||
Cache local = (Cache)getCache();
|
if (localCache.contendedMonitor == null &&
|
||||||
|
!localCache.triedCurrentContended) {
|
||||||
if (local != null && local.triedCurrentContended) {
|
localCache.contendedMonitor = JDWP.ThreadReference.CurrentContendedMonitor.
|
||||||
monitor = local.contendedMonitor;
|
|
||||||
} else {
|
|
||||||
monitor = JDWP.ThreadReference.CurrentContendedMonitor.
|
|
||||||
process(vm, this).monitor;
|
process(vm, this).monitor;
|
||||||
if (local != null) {
|
localCache.triedCurrentContended = true;
|
||||||
local.triedCurrentContended = true;
|
if ((localCache.contendedMonitor != null) &&
|
||||||
local.contendedMonitor = monitor;
|
((vm.traceFlags & vm.TRACE_OBJREFS) != 0)) {
|
||||||
if ((monitor != null) &&
|
vm.printTrace(description() +
|
||||||
((vm.traceFlags & vm.TRACE_OBJREFS) != 0)) {
|
" temporarily caching contended monitor"+
|
||||||
vm.printTrace(description() +
|
" (id = " + localCache.contendedMonitor.uniqueID() + ")");
|
||||||
" temporarily caching contended monitor"+
|
|
||||||
" (id = " + monitor.uniqueID() + ")");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (JDWPException exc) {
|
} catch (JDWPException exc) {
|
||||||
@ -450,40 +461,31 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl
|
|||||||
throw exc.toJDIException();
|
throw exc.toJDIException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return monitor;
|
return localCache.contendedMonitor;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<MonitorInfo> ownedMonitorsAndFrames() throws IncompatibleThreadStateException {
|
public List<MonitorInfo> ownedMonitorsAndFrames() throws IncompatibleThreadStateException {
|
||||||
List<MonitorInfo> monitors = null;
|
|
||||||
try {
|
try {
|
||||||
Cache local = (Cache)getCache();
|
if (localCache.ownedMonitorsInfo == null) {
|
||||||
|
|
||||||
if (local != null) {
|
|
||||||
monitors = local.ownedMonitorsInfo;
|
|
||||||
}
|
|
||||||
if (monitors == null) {
|
|
||||||
JDWP.ThreadReference.OwnedMonitorsStackDepthInfo.monitor[] minfo;
|
JDWP.ThreadReference.OwnedMonitorsStackDepthInfo.monitor[] minfo;
|
||||||
minfo = JDWP.ThreadReference.OwnedMonitorsStackDepthInfo.process(vm, this).owned;
|
minfo = JDWP.ThreadReference.OwnedMonitorsStackDepthInfo.process(vm, this).owned;
|
||||||
|
|
||||||
monitors = new ArrayList<MonitorInfo>(minfo.length);
|
localCache.ownedMonitorsInfo = new ArrayList<MonitorInfo>(minfo.length);
|
||||||
|
|
||||||
for (int i=0; i < minfo.length; i++) {
|
for (int i=0; i < minfo.length; i++) {
|
||||||
JDWP.ThreadReference.OwnedMonitorsStackDepthInfo.monitor mi =
|
JDWP.ThreadReference.OwnedMonitorsStackDepthInfo.monitor mi =
|
||||||
minfo[i];
|
minfo[i];
|
||||||
MonitorInfo mon = new MonitorInfoImpl(vm, minfo[i].monitor, this, minfo[i].stack_depth);
|
MonitorInfo mon = new MonitorInfoImpl(vm, minfo[i].monitor, this, minfo[i].stack_depth);
|
||||||
monitors.add(mon);
|
localCache.ownedMonitorsInfo.add(mon);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (local != null) {
|
if ((vm.traceFlags & vm.TRACE_OBJREFS) != 0) {
|
||||||
local.ownedMonitorsInfo = monitors;
|
vm.printTrace(description() +
|
||||||
if ((vm.traceFlags & vm.TRACE_OBJREFS) != 0) {
|
" temporarily caching owned monitors"+
|
||||||
vm.printTrace(description() +
|
" (count = " + localCache.ownedMonitorsInfo.size() + ")");
|
||||||
" temporarily caching owned monitors"+
|
|
||||||
" (count = " + monitors.size() + ")");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
} catch (JDWPException exc) {
|
} catch (JDWPException exc) {
|
||||||
switch (exc.errorCode()) {
|
switch (exc.errorCode()) {
|
||||||
case JDWP.Error.THREAD_NOT_SUSPENDED:
|
case JDWP.Error.THREAD_NOT_SUSPENDED:
|
||||||
@ -493,7 +495,7 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl
|
|||||||
throw exc.toJDIException();
|
throw exc.toJDIException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return monitors;
|
return localCache.ownedMonitorsInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void popFrames(StackFrame frame) throws IncompatibleThreadStateException {
|
public void popFrames(StackFrame frame) throws IncompatibleThreadStateException {
|
||||||
@ -511,7 +513,7 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void forceEarlyReturn(Value returnValue) throws InvalidTypeException,
|
public void forceEarlyReturn(Value returnValue) throws InvalidTypeException,
|
||||||
ClassNotLoadedException,
|
ClassNotLoadedException,
|
||||||
IncompatibleThreadStateException {
|
IncompatibleThreadStateException {
|
||||||
if (!vm.canForceEarlyReturn()) {
|
if (!vm.canForceEarlyReturn()) {
|
||||||
throw new UnsupportedOperationException(
|
throw new UnsupportedOperationException(
|
||||||
@ -604,6 +606,9 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl
|
|||||||
iter.remove();
|
iter.remove();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Discard our local cache
|
||||||
|
resetLocalCache();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,10 +38,18 @@ class VMAction extends EventObject {
|
|||||||
static final int VM_NOT_SUSPENDED = 2;
|
static final int VM_NOT_SUSPENDED = 2;
|
||||||
|
|
||||||
int id;
|
int id;
|
||||||
|
ThreadReference resumingThread;
|
||||||
|
|
||||||
VMAction(VirtualMachine vm, int id) {
|
VMAction(VirtualMachine vm, int id) {
|
||||||
|
this(vm, null, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// For id = VM_NOT_SUSPENDED, if resumingThread != null, then it is
|
||||||
|
// the only thread that is being resumed.
|
||||||
|
VMAction(VirtualMachine vm, ThreadReference resumingThread, int id) {
|
||||||
super(vm);
|
super(vm);
|
||||||
this.id = id;
|
this.id = id;
|
||||||
|
this.resumingThread = resumingThread;
|
||||||
}
|
}
|
||||||
VirtualMachine vm() {
|
VirtualMachine vm() {
|
||||||
return (VirtualMachine)getSource();
|
return (VirtualMachine)getSource();
|
||||||
@ -49,4 +57,8 @@ class VMAction extends EventObject {
|
|||||||
int id() {
|
int id() {
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ThreadReference resumingThread() {
|
||||||
|
return resumingThread;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -116,16 +116,25 @@ class VMState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tell listeners to invalidate suspend-sensitive caches.
|
* All threads are resuming
|
||||||
*/
|
*/
|
||||||
synchronized void thaw() {
|
void thaw() {
|
||||||
|
thaw(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tell listeners to invalidate suspend-sensitive caches.
|
||||||
|
* If resumingThread != null, then only that thread is being
|
||||||
|
* resumed.
|
||||||
|
*/
|
||||||
|
synchronized void thaw(ThreadReference resumingThread) {
|
||||||
if (cache != null) {
|
if (cache != null) {
|
||||||
if ((vm.traceFlags & vm.TRACE_OBJREFS) != 0) {
|
if ((vm.traceFlags & vm.TRACE_OBJREFS) != 0) {
|
||||||
vm.printTrace("Clearing VM suspended cache");
|
vm.printTrace("Clearing VM suspended cache");
|
||||||
}
|
}
|
||||||
disableCache();
|
disableCache();
|
||||||
}
|
}
|
||||||
processVMAction(new VMAction(vm, VMAction.VM_NOT_SUSPENDED));
|
processVMAction(new VMAction(vm, resumingThread, VMAction.VM_NOT_SUSPENDED));
|
||||||
}
|
}
|
||||||
|
|
||||||
private synchronized void processVMAction(VMAction action) {
|
private synchronized void processVMAction(VMAction action) {
|
||||||
|
@ -146,8 +146,9 @@ class VirtualMachineImpl extends MirrorImpl
|
|||||||
public boolean threadResumable(ThreadAction action) {
|
public boolean threadResumable(ThreadAction action) {
|
||||||
/*
|
/*
|
||||||
* If any thread is resumed, the VM is considered not suspended.
|
* If any thread is resumed, the VM is considered not suspended.
|
||||||
|
* Just one thread is being resumed so pass it to thaw.
|
||||||
*/
|
*/
|
||||||
state.thaw();
|
state.thaw(action.thread());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,7 +25,8 @@
|
|||||||
* @test
|
* @test
|
||||||
* @bug 6230699
|
* @bug 6230699
|
||||||
* @summary Test ThreadReference.ownedMonitorsAndFrames()
|
* @summary Test ThreadReference.ownedMonitorsAndFrames()
|
||||||
*
|
* @bug 6701700
|
||||||
|
* @summary MonitorInfo objects aren't invalidated when the owning thread is resumed
|
||||||
* @author Swamy Venkataramanappa
|
* @author Swamy Venkataramanappa
|
||||||
*
|
*
|
||||||
* @run build TestScaffold VMConnection TargetListener TargetAdapter
|
* @run build TestScaffold VMConnection TargetListener TargetAdapter
|
||||||
@ -100,15 +101,15 @@ public class MonitorFrameInfo extends TestScaffold {
|
|||||||
|
|
||||||
if (!mainThread.frame(0).location().method().name()
|
if (!mainThread.frame(0).location().method().name()
|
||||||
.equals("foo3")) {
|
.equals("foo3")) {
|
||||||
failure("frame failed");
|
failure("FAILED: frame failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mainThread.frames().size() != (initialSize + 3)) {
|
if (mainThread.frames().size() != (initialSize + 3)) {
|
||||||
failure("frames size failed");
|
failure("FAILED: frames size failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mainThread.frames().size() != mainThread.frameCount()) {
|
if (mainThread.frames().size() != mainThread.frameCount()) {
|
||||||
failure("frames size not equal to frameCount");
|
failure("FAILED: frames size not equal to frameCount");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Test monitor frame info.
|
/* Test monitor frame info.
|
||||||
@ -119,13 +120,32 @@ public class MonitorFrameInfo extends TestScaffold {
|
|||||||
if (monitors.size() != expectedCount) {
|
if (monitors.size() != expectedCount) {
|
||||||
failure("monitors count is not equal to expected count");
|
failure("monitors count is not equal to expected count");
|
||||||
}
|
}
|
||||||
|
MonitorInfo mon = null;
|
||||||
for (int j=0; j < monitors.size(); j++) {
|
for (int j=0; j < monitors.size(); j++) {
|
||||||
MonitorInfo mon = (MonitorInfo)monitors.get(j);
|
mon = (MonitorInfo)monitors.get(j);
|
||||||
System.out.println("Monitor obj " + mon.monitor() + "depth =" +mon.stackDepth());
|
System.out.println("Monitor obj " + mon.monitor() + "depth =" +mon.stackDepth());
|
||||||
if (mon.stackDepth() != expectedDepth[j]) {
|
if (mon.stackDepth() != expectedDepth[j]) {
|
||||||
failure("monitor stack depth is not equal to expected depth");
|
failure("FAILED: monitor stack depth is not equal to expected depth");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The last gotten monInfo is in mon. When we resume the thread,
|
||||||
|
// it should become invalid. We will step out of the top frame
|
||||||
|
// so that the frame depth in this mon object will no longer be correct.
|
||||||
|
// That is why the monInfo's have to become invalid when the thread is
|
||||||
|
// resumed.
|
||||||
|
stepOut(mainThread);
|
||||||
|
boolean ok = false;
|
||||||
|
try {
|
||||||
|
System.out.println("*** Saved Monitor obj " + mon.monitor() + "depth =" +mon.stackDepth());
|
||||||
|
} catch(InvalidStackFrameException ee) {
|
||||||
|
// ok
|
||||||
|
ok = true;
|
||||||
|
System.out.println("Got expected InvalidStackFrameException after a resume");
|
||||||
|
}
|
||||||
|
if (!ok) {
|
||||||
|
failure("FAILED: MonitorInfo object was not invalidated by a resume");
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
System.out.println("can not get monitors frame info");
|
System.out.println("can not get monitors frame info");
|
||||||
}
|
}
|
||||||
|
233
jdk/test/com/sun/jdi/ResumeOneThreadTest.java
Normal file
233
jdk/test/com/sun/jdi/ResumeOneThreadTest.java
Normal file
@ -0,0 +1,233 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2008 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
* @bug 6700889
|
||||||
|
* @summary Thread resume invalidates all stack frames, even from other threads
|
||||||
|
*
|
||||||
|
* @author jjh
|
||||||
|
*
|
||||||
|
* @run build TestScaffold VMConnection TargetListener TargetAdapter
|
||||||
|
* @run compile -g ResumeOneThreadTest.java
|
||||||
|
* @run main ResumeOneThreadTest
|
||||||
|
*/
|
||||||
|
import com.sun.jdi.*;
|
||||||
|
import com.sun.jdi.event.*;
|
||||||
|
import com.sun.jdi.request.*;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
class ResumeOneThreadTarg extends Thread {
|
||||||
|
static String name1 = "Thread 1";
|
||||||
|
static String name2 = "Thread 2";
|
||||||
|
|
||||||
|
public ResumeOneThreadTarg(String name) {
|
||||||
|
super(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
System.out.println(" Debuggee: Howdy!");
|
||||||
|
ResumeOneThreadTarg t1 = new ResumeOneThreadTarg(name1);
|
||||||
|
ResumeOneThreadTarg t2 = new ResumeOneThreadTarg(name2);
|
||||||
|
|
||||||
|
t1.start();
|
||||||
|
t2.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
// This just starts two threads. Each runs to a bkpt.
|
||||||
|
public void run() {
|
||||||
|
if (getName().equals(name1)) {
|
||||||
|
run1();
|
||||||
|
} else {
|
||||||
|
run2();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void bkpt1(String p1) {
|
||||||
|
System.out.println(" Debuggee: bkpt 1");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void run1() {
|
||||||
|
bkpt1("Hello Alviso!");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public void bkpt2() {
|
||||||
|
System.out.println(" Debuggee: bkpt 2");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void run2() {
|
||||||
|
bkpt2();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/********** test program **********/
|
||||||
|
|
||||||
|
public class ResumeOneThreadTest extends TestScaffold {
|
||||||
|
ReferenceType targetClass;
|
||||||
|
ThreadReference mainThread;
|
||||||
|
|
||||||
|
BreakpointRequest request1;
|
||||||
|
BreakpointRequest request2;
|
||||||
|
|
||||||
|
ThreadReference thread1 = null;
|
||||||
|
ThreadReference thread2 = null;;
|
||||||
|
boolean theVMisDead = false;
|
||||||
|
|
||||||
|
ResumeOneThreadTest (String args[]) {
|
||||||
|
super(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
new ResumeOneThreadTest(args).startTests();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
synchronized public void breakpointReached(BreakpointEvent event) {
|
||||||
|
println("-- Got bkpt at: " + event.location());
|
||||||
|
ThreadReference eventThread = event.thread();
|
||||||
|
|
||||||
|
if (eventThread.name().equals(ResumeOneThreadTarg.name1)) {
|
||||||
|
thread1 = eventThread;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (eventThread.name().equals(ResumeOneThreadTarg.name2)) {
|
||||||
|
thread2 = eventThread;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void vmDied(VMDeathEvent event) {
|
||||||
|
theVMisDead = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized public void eventSetComplete(EventSet set) {
|
||||||
|
if (theVMisDead) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (thread1 == null || thread2 == null) {
|
||||||
|
// Don't do a set.resume(), just let the other thread
|
||||||
|
// keep running until it hits its bkpt.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Both threads are stopped at their bkpts. Get a StackFrame from
|
||||||
|
// Thread 1 then resume Thread 2 and verify that the saved StackFrame is
|
||||||
|
// still valid.
|
||||||
|
|
||||||
|
// suspend everything.
|
||||||
|
println("-- All threads suspended");
|
||||||
|
vm().suspend();
|
||||||
|
|
||||||
|
StackFrame t1sf0 = null;
|
||||||
|
try {
|
||||||
|
t1sf0 = thread1.frame(0);
|
||||||
|
} catch (IncompatibleThreadStateException ee) {
|
||||||
|
failure("FAILED: Exception: " + ee);
|
||||||
|
}
|
||||||
|
|
||||||
|
println("-- t1sf0 args: " + t1sf0.getArgumentValues());
|
||||||
|
|
||||||
|
// Ok, we have a StackFrame for thread 1. Resume just thread 2
|
||||||
|
// Note that thread 2 has been suspended twice - by the SUSPEND_ALL
|
||||||
|
// bkpt, and by the above vm().suspend(), so we have to resume
|
||||||
|
// it twice.
|
||||||
|
request2.disable();
|
||||||
|
|
||||||
|
thread2.resume();
|
||||||
|
thread2.resume();
|
||||||
|
println("-- Did Resume on thread 2");
|
||||||
|
|
||||||
|
// Can we get frames for thread1?
|
||||||
|
try {
|
||||||
|
StackFrame t1sf0_1 = thread1.frame(0);
|
||||||
|
if (!t1sf0.equals(t1sf0_1)) {
|
||||||
|
failure("FAILED: Got a different frame 0 for thread 1 after resuming thread 2");
|
||||||
|
}
|
||||||
|
} catch (IncompatibleThreadStateException ee) {
|
||||||
|
failure("FAILED: Could not get frames for thread 1: Exception: " + ee);
|
||||||
|
} catch (Exception ee) {
|
||||||
|
failure("FAILED: Could not get frames for thread 1: Exception: " + ee);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
try {
|
||||||
|
println("-- t1sf0 args: " + t1sf0.getArgumentValues());
|
||||||
|
} catch (InvalidStackFrameException ee) {
|
||||||
|
// This is the failure.
|
||||||
|
failure("FAILED Got InvalidStackFrameException");
|
||||||
|
vm().dispose();
|
||||||
|
throw(ee);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Let the debuggee finish
|
||||||
|
request1.disable();
|
||||||
|
thread1.resume();
|
||||||
|
vm().resume();
|
||||||
|
println("--------------");
|
||||||
|
}
|
||||||
|
|
||||||
|
/********** test core **********/
|
||||||
|
|
||||||
|
protected void runTests() throws Exception {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get to the top of main()
|
||||||
|
* to determine targetClass and mainThread
|
||||||
|
*/
|
||||||
|
BreakpointEvent bpe = startToMain("ResumeOneThreadTarg");
|
||||||
|
targetClass = bpe.location().declaringType();
|
||||||
|
mainThread = bpe.thread();
|
||||||
|
EventRequestManager erm = vm().eventRequestManager();
|
||||||
|
final Thread mainThread = Thread.currentThread();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set event requests
|
||||||
|
*/
|
||||||
|
|
||||||
|
Location loc1 = findMethod(targetClass, "bkpt1", "(Ljava/lang/String;)V").location();
|
||||||
|
request1 = erm.createBreakpointRequest(loc1);
|
||||||
|
request1.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD);
|
||||||
|
request1.enable();
|
||||||
|
|
||||||
|
Location loc2 = findMethod(targetClass, "bkpt2", "()V").location();
|
||||||
|
request2 = erm.createBreakpointRequest(loc2);
|
||||||
|
request2.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD);
|
||||||
|
request2.enable();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* resume the target, listening for events
|
||||||
|
*/
|
||||||
|
listenUntilVMDisconnect();
|
||||||
|
/*
|
||||||
|
* deal with results of test
|
||||||
|
* if anything has called failure("foo") testFailed will be true
|
||||||
|
*/
|
||||||
|
if (!testFailed) {
|
||||||
|
println("ResumeOneThreadTest: passed");
|
||||||
|
} else {
|
||||||
|
throw new Exception("ResumeOneThreadTest: failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user