Merge
This commit is contained in:
commit
46aa35c92c
@ -40,11 +40,12 @@ public class MonitorInfoImpl extends MirrorImpl
|
||||
int stack_depth;
|
||||
|
||||
MonitorInfoImpl(VirtualMachine vm, ObjectReference mon,
|
||||
ThreadReference thread, int dpth) {
|
||||
ThreadReferenceImpl thread, int dpth) {
|
||||
super(vm);
|
||||
this.monitor = mon;
|
||||
this.thread = thread;
|
||||
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_BREAK = 0x2;
|
||||
|
||||
private ThreadGroupReference threadGroup;
|
||||
private int suspendedZombieCount = 0;
|
||||
|
||||
// This is cached only while the VM is suspended
|
||||
private static class Cache extends ObjectReferenceImpl.Cache {
|
||||
String name = null;
|
||||
/*
|
||||
* Some objects can only be created while a thread is suspended and are valid
|
||||
* 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;
|
||||
List<StackFrame> frames = null;
|
||||
int framesStart = -1;
|
||||
@ -52,6 +74,17 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl
|
||||
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() {
|
||||
return new Cache();
|
||||
}
|
||||
@ -59,8 +92,10 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl
|
||||
// Listeners - synchronized on vm.state()
|
||||
private List<WeakReference<ThreadListener>> listeners = new ArrayList<WeakReference<ThreadListener>>();
|
||||
|
||||
|
||||
ThreadReferenceImpl(VirtualMachine aVm, long aRef) {
|
||||
super(aVm,aRef);
|
||||
resetLocalCache();
|
||||
vm.state().addListener(this);
|
||||
}
|
||||
|
||||
@ -72,10 +107,24 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl
|
||||
* VMListener implementation
|
||||
*/
|
||||
public boolean vmNotSuspended(VMAction action) {
|
||||
synchronized (vm.state()) {
|
||||
processThreadAction(new ThreadAction(this,
|
||||
ThreadAction.THREAD_RESUMABLE));
|
||||
if (action.resumingThread() == null) {
|
||||
// all threads are being resumed
|
||||
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);
|
||||
}
|
||||
|
||||
@ -191,23 +240,19 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl
|
||||
}
|
||||
|
||||
private JDWP.ThreadReference.Status jdwpStatus() {
|
||||
JDWP.ThreadReference.Status status = null;
|
||||
JDWP.ThreadReference.Status myStatus = localCache.status;
|
||||
try {
|
||||
Cache local = (Cache)getCache();
|
||||
|
||||
if (local != null) {
|
||||
status = local.status;
|
||||
}
|
||||
if (status == null) {
|
||||
status = JDWP.ThreadReference.Status.process(vm, this);
|
||||
if (local != null) {
|
||||
local.status = status;
|
||||
if (myStatus == null) {
|
||||
myStatus = JDWP.ThreadReference.Status.process(vm, this);
|
||||
if ((myStatus.suspendStatus & SUSPEND_STATUS_SUSPENDED) != 0) {
|
||||
// thread is suspended, we can cache the status.
|
||||
localCache.status = myStatus;
|
||||
}
|
||||
}
|
||||
} catch (JDWPException exc) {
|
||||
} catch (JDWPException exc) {
|
||||
throw exc.toJDIException();
|
||||
}
|
||||
return status;
|
||||
return myStatus;
|
||||
}
|
||||
|
||||
public int status() {
|
||||
@ -245,8 +290,7 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl
|
||||
|
||||
public ThreadGroupReference threadGroup() {
|
||||
/*
|
||||
* Thread group can't change, so it's cached more conventionally
|
||||
* than other things in this class.
|
||||
* Thread group can't change, so it's cached once and for all.
|
||||
*/
|
||||
if (threadGroup == null) {
|
||||
try {
|
||||
@ -260,19 +304,10 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl
|
||||
}
|
||||
|
||||
public int frameCount() throws IncompatibleThreadStateException {
|
||||
int frameCount = -1;
|
||||
try {
|
||||
Cache local = (Cache)getCache();
|
||||
|
||||
if (local != null) {
|
||||
frameCount = local.frameCount;
|
||||
}
|
||||
if (frameCount == -1) {
|
||||
frameCount = JDWP.ThreadReference.FrameCount
|
||||
if (localCache.frameCount == -1) {
|
||||
localCache.frameCount = JDWP.ThreadReference.FrameCount
|
||||
.process(vm, this).frameCount;
|
||||
if (local != null) {
|
||||
local.frameCount = frameCount;
|
||||
}
|
||||
}
|
||||
} catch (JDWPException exc) {
|
||||
switch (exc.errorCode()) {
|
||||
@ -283,7 +318,7 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl
|
||||
throw exc.toJDIException();
|
||||
}
|
||||
}
|
||||
return frameCount;
|
||||
return localCache.frameCount;
|
||||
}
|
||||
|
||||
public List<StackFrame> frames() throws IncompatibleThreadStateException {
|
||||
@ -297,23 +332,25 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl
|
||||
|
||||
/**
|
||||
* 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,
|
||||
int start, int length, List frames) {
|
||||
if (start < local.framesStart) {
|
||||
private boolean isSubrange(LocalCache localCache,
|
||||
int start, int length) {
|
||||
if (start < localCache.framesStart) {
|
||||
return false;
|
||||
}
|
||||
if (length == -1) {
|
||||
return (local.framesLength == -1);
|
||||
return (localCache.framesLength == -1);
|
||||
}
|
||||
if (local.framesLength == -1) {
|
||||
if ((start + length) > (local.framesStart + frames.size())) {
|
||||
if (localCache.framesLength == -1) {
|
||||
if ((start + length) > (localCache.framesStart +
|
||||
localCache.frames.size())) {
|
||||
throw new IndexOutOfBoundsException();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return ((start + length) <= (local.framesStart + local.framesLength));
|
||||
return ((start + length) <= (localCache.framesStart + localCache.framesLength));
|
||||
}
|
||||
|
||||
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
|
||||
* remaining frames.
|
||||
*/
|
||||
private List<StackFrame> privateFrames(int start, int length)
|
||||
synchronized private List<StackFrame> privateFrames(int start, int length)
|
||||
throws IncompatibleThreadStateException {
|
||||
List<StackFrame> frames = null;
|
||||
try {
|
||||
Cache local = (Cache)getCache();
|
||||
|
||||
if (local != null) {
|
||||
frames = local.frames;
|
||||
}
|
||||
if (frames == null || !isSubrange(local, start, length, frames)) {
|
||||
// Lock must be held while creating stack frames so if that two threads
|
||||
// do this at the same time, one won't clobber the subset created by the other.
|
||||
|
||||
try {
|
||||
if (localCache.frames == null || !isSubrange(localCache, start, length)) {
|
||||
JDWP.ThreadReference.Frames.Frame[] jdwpFrames
|
||||
= JDWP.ThreadReference.Frames.
|
||||
process(vm, this, start, length).frames;
|
||||
process(vm, this, start, length).frames;
|
||||
int count = jdwpFrames.length;
|
||||
frames = new ArrayList<StackFrame>(count);
|
||||
localCache.frames = new ArrayList<StackFrame>(count);
|
||||
|
||||
// Lock must be held while creating stack frames.
|
||||
// so that a resume will not resume a partially
|
||||
// created stack.
|
||||
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);
|
||||
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
|
||||
localCache.frames.add(frame);
|
||||
}
|
||||
if (local != null) {
|
||||
local.frames = frames;
|
||||
local.framesStart = start;
|
||||
local.framesLength = length;
|
||||
}
|
||||
localCache.framesStart = start;
|
||||
localCache.framesLength = length;
|
||||
return Collections.unmodifiableList(localCache.frames);
|
||||
} else {
|
||||
int fromIndex = start - local.framesStart;
|
||||
int fromIndex = start - localCache.framesStart;
|
||||
int toIndex;
|
||||
if (length == -1) {
|
||||
toIndex = frames.size() - fromIndex;
|
||||
toIndex = localCache.frames.size() - fromIndex;
|
||||
} else {
|
||||
toIndex = fromIndex + length;
|
||||
}
|
||||
frames = frames.subList(fromIndex, toIndex);
|
||||
return Collections.unmodifiableList(localCache.frames.subList(fromIndex, toIndex));
|
||||
}
|
||||
} catch (JDWPException exc) {
|
||||
switch (exc.errorCode()) {
|
||||
@ -384,28 +412,18 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl
|
||||
throw exc.toJDIException();
|
||||
}
|
||||
}
|
||||
return Collections.unmodifiableList(frames);
|
||||
}
|
||||
|
||||
public List<ObjectReference> ownedMonitors() throws IncompatibleThreadStateException {
|
||||
List<ObjectReference> monitors = null;
|
||||
try {
|
||||
Cache local = (Cache)getCache();
|
||||
|
||||
if (local != null) {
|
||||
monitors = local.ownedMonitors;
|
||||
}
|
||||
if (monitors == null) {
|
||||
monitors = Arrays.asList(
|
||||
if (localCache.ownedMonitors == null) {
|
||||
localCache.ownedMonitors = Arrays.asList(
|
||||
(ObjectReference[])JDWP.ThreadReference.OwnedMonitors.
|
||||
process(vm, this).owned);
|
||||
if (local != null) {
|
||||
local.ownedMonitors = monitors;
|
||||
if ((vm.traceFlags & vm.TRACE_OBJREFS) != 0) {
|
||||
vm.printTrace(description() +
|
||||
" temporarily caching owned monitors"+
|
||||
" (count = " + monitors.size() + ")");
|
||||
}
|
||||
if ((vm.traceFlags & vm.TRACE_OBJREFS) != 0) {
|
||||
vm.printTrace(description() +
|
||||
" temporarily caching owned monitors"+
|
||||
" (count = " + localCache.ownedMonitors.size() + ")");
|
||||
}
|
||||
}
|
||||
} catch (JDWPException exc) {
|
||||
@ -417,29 +435,22 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl
|
||||
throw exc.toJDIException();
|
||||
}
|
||||
}
|
||||
return monitors;
|
||||
return localCache.ownedMonitors;
|
||||
}
|
||||
|
||||
public ObjectReference currentContendedMonitor()
|
||||
throws IncompatibleThreadStateException {
|
||||
ObjectReference monitor = null;
|
||||
try {
|
||||
Cache local = (Cache)getCache();
|
||||
|
||||
if (local != null && local.triedCurrentContended) {
|
||||
monitor = local.contendedMonitor;
|
||||
} else {
|
||||
monitor = JDWP.ThreadReference.CurrentContendedMonitor.
|
||||
if (localCache.contendedMonitor == null &&
|
||||
!localCache.triedCurrentContended) {
|
||||
localCache.contendedMonitor = JDWP.ThreadReference.CurrentContendedMonitor.
|
||||
process(vm, this).monitor;
|
||||
if (local != null) {
|
||||
local.triedCurrentContended = true;
|
||||
local.contendedMonitor = monitor;
|
||||
if ((monitor != null) &&
|
||||
((vm.traceFlags & vm.TRACE_OBJREFS) != 0)) {
|
||||
vm.printTrace(description() +
|
||||
" temporarily caching contended monitor"+
|
||||
" (id = " + monitor.uniqueID() + ")");
|
||||
}
|
||||
localCache.triedCurrentContended = true;
|
||||
if ((localCache.contendedMonitor != null) &&
|
||||
((vm.traceFlags & vm.TRACE_OBJREFS) != 0)) {
|
||||
vm.printTrace(description() +
|
||||
" temporarily caching contended monitor"+
|
||||
" (id = " + localCache.contendedMonitor.uniqueID() + ")");
|
||||
}
|
||||
}
|
||||
} catch (JDWPException exc) {
|
||||
@ -450,40 +461,31 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl
|
||||
throw exc.toJDIException();
|
||||
}
|
||||
}
|
||||
return monitor;
|
||||
return localCache.contendedMonitor;
|
||||
}
|
||||
|
||||
public List<MonitorInfo> ownedMonitorsAndFrames() throws IncompatibleThreadStateException {
|
||||
List<MonitorInfo> monitors = null;
|
||||
try {
|
||||
Cache local = (Cache)getCache();
|
||||
|
||||
if (local != null) {
|
||||
monitors = local.ownedMonitorsInfo;
|
||||
}
|
||||
if (monitors == null) {
|
||||
if (localCache.ownedMonitorsInfo == null) {
|
||||
JDWP.ThreadReference.OwnedMonitorsStackDepthInfo.monitor[] minfo;
|
||||
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++) {
|
||||
JDWP.ThreadReference.OwnedMonitorsStackDepthInfo.monitor mi =
|
||||
minfo[i];
|
||||
MonitorInfo mon = new MonitorInfoImpl(vm, minfo[i].monitor, this, minfo[i].stack_depth);
|
||||
monitors.add(mon);
|
||||
localCache.ownedMonitorsInfo.add(mon);
|
||||
}
|
||||
|
||||
if (local != null) {
|
||||
local.ownedMonitorsInfo = monitors;
|
||||
if ((vm.traceFlags & vm.TRACE_OBJREFS) != 0) {
|
||||
vm.printTrace(description() +
|
||||
" temporarily caching owned monitors"+
|
||||
" (count = " + monitors.size() + ")");
|
||||
if ((vm.traceFlags & vm.TRACE_OBJREFS) != 0) {
|
||||
vm.printTrace(description() +
|
||||
" temporarily caching owned monitors"+
|
||||
" (count = " + localCache.ownedMonitorsInfo.size() + ")");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
} catch (JDWPException exc) {
|
||||
switch (exc.errorCode()) {
|
||||
case JDWP.Error.THREAD_NOT_SUSPENDED:
|
||||
@ -493,7 +495,7 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl
|
||||
throw exc.toJDIException();
|
||||
}
|
||||
}
|
||||
return monitors;
|
||||
return localCache.ownedMonitorsInfo;
|
||||
}
|
||||
|
||||
public void popFrames(StackFrame frame) throws IncompatibleThreadStateException {
|
||||
@ -511,7 +513,7 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl
|
||||
}
|
||||
|
||||
public void forceEarlyReturn(Value returnValue) throws InvalidTypeException,
|
||||
ClassNotLoadedException,
|
||||
ClassNotLoadedException,
|
||||
IncompatibleThreadStateException {
|
||||
if (!vm.canForceEarlyReturn()) {
|
||||
throw new UnsupportedOperationException(
|
||||
@ -604,6 +606,9 @@ public class ThreadReferenceImpl extends ObjectReferenceImpl
|
||||
iter.remove();
|
||||
}
|
||||
}
|
||||
|
||||
// Discard our local cache
|
||||
resetLocalCache();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -38,10 +38,18 @@ class VMAction extends EventObject {
|
||||
static final int VM_NOT_SUSPENDED = 2;
|
||||
|
||||
int id;
|
||||
ThreadReference resumingThread;
|
||||
|
||||
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);
|
||||
this.id = id;
|
||||
this.resumingThread = resumingThread;
|
||||
}
|
||||
VirtualMachine vm() {
|
||||
return (VirtualMachine)getSource();
|
||||
@ -49,4 +57,8 @@ class VMAction extends EventObject {
|
||||
int 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 ((vm.traceFlags & vm.TRACE_OBJREFS) != 0) {
|
||||
vm.printTrace("Clearing VM suspended cache");
|
||||
}
|
||||
disableCache();
|
||||
}
|
||||
processVMAction(new VMAction(vm, VMAction.VM_NOT_SUSPENDED));
|
||||
processVMAction(new VMAction(vm, resumingThread, VMAction.VM_NOT_SUSPENDED));
|
||||
}
|
||||
|
||||
private synchronized void processVMAction(VMAction action) {
|
||||
|
@ -146,8 +146,9 @@ class VirtualMachineImpl extends MirrorImpl
|
||||
public boolean threadResumable(ThreadAction action) {
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
|
@ -25,7 +25,8 @@
|
||||
* @test
|
||||
* @bug 6230699
|
||||
* @summary Test ThreadReference.ownedMonitorsAndFrames()
|
||||
*
|
||||
* @bug 6701700
|
||||
* @summary MonitorInfo objects aren't invalidated when the owning thread is resumed
|
||||
* @author Swamy Venkataramanappa
|
||||
*
|
||||
* @run build TestScaffold VMConnection TargetListener TargetAdapter
|
||||
@ -100,15 +101,15 @@ public class MonitorFrameInfo extends TestScaffold {
|
||||
|
||||
if (!mainThread.frame(0).location().method().name()
|
||||
.equals("foo3")) {
|
||||
failure("frame failed");
|
||||
failure("FAILED: frame failed");
|
||||
}
|
||||
|
||||
if (mainThread.frames().size() != (initialSize + 3)) {
|
||||
failure("frames size failed");
|
||||
failure("FAILED: frames size failed");
|
||||
}
|
||||
|
||||
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.
|
||||
@ -119,13 +120,32 @@ public class MonitorFrameInfo extends TestScaffold {
|
||||
if (monitors.size() != expectedCount) {
|
||||
failure("monitors count is not equal to expected count");
|
||||
}
|
||||
MonitorInfo mon = null;
|
||||
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());
|
||||
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 {
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
@ -115,7 +115,7 @@ class VMConnection {
|
||||
return cmdLine;
|
||||
}
|
||||
// Insert the options at position 1. Blanks in args are not allowed!
|
||||
String[] v1 = opts.split(" ");
|
||||
String[] v1 = opts.split(" +");
|
||||
String[] retVal = new String[cmdLine.length + v1.length];
|
||||
retVal[0] = cmdLine[0];
|
||||
System.arraycopy(v1, 0, retVal, 1, v1.length);
|
||||
|
Loading…
x
Reference in New Issue
Block a user