2
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
4
* Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
6
* The contents of this file are subject to the terms of either the GNU
7
* General Public License Version 2 only ("GPL") or the Common
8
* Development and Distribution License("CDDL") (collectively, the
9
* "License"). You may not use this file except in compliance with the
10
* License. You can obtain a copy of the License at
11
* http://www.netbeans.org/cddl-gplv2.html
12
* or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
13
* specific language governing permissions and limitations under the
14
* License. When distributing the software, include this License Header
15
* Notice in each file and include the License file at
16
* nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
17
* particular file as subject to the "Classpath" exception as provided
18
* by Sun in the GPL Version 2 section of the License file that
19
* accompanied this code. If applicable, add the following below the
20
* License Header, with the fields enclosed by brackets [] replaced by
21
* your own identifying information:
22
* "Portions Copyrighted [year] [name of copyright owner]"
26
* The Original Software is NetBeans. The Initial Developer of the Original
27
* Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
28
* Microsystems, Inc. All Rights Reserved.
30
* If you wish your version of this file to be governed by only the CDDL
31
* or only the GPL Version 2, indicate your decision by adding
32
* "[Contributor] elects to include this software in this distribution
33
* under the [CDDL or GPL Version 2] license." If you do not indicate a
34
* single choice of license, a recipient has the option to distribute
35
* your version of this file under either the CDDL, the GPL Version 2 or
36
* to extend the choice of license to its licensees as provided above.
37
* However, if you add GPL Version 2 code and therefore, elected the GPL
38
* Version 2 license, then the option applies only if the new code is
39
* made subject to such option by the copyright holder.
42
package org.netbeans.modules.debugger.jpda.models;
44
import com.sun.jdi.AbsentInformationException;
45
import com.sun.jdi.IncompatibleThreadStateException;
46
import com.sun.jdi.InvalidStackFrameException;
47
import com.sun.jdi.InternalException;
48
import com.sun.jdi.NativeMethodException;
49
import com.sun.jdi.ObjectCollectedException;
50
import com.sun.jdi.ObjectReference;
51
import com.sun.jdi.StackFrame;
52
import com.sun.jdi.ThreadGroupReference;
53
import com.sun.jdi.ThreadReference;
54
import com.sun.jdi.VMDisconnectedException;
56
import java.beans.PropertyChangeListener;
57
import java.beans.PropertyChangeSupport;
58
import java.beans.PropertyVetoException;
59
import java.util.ArrayList;
60
import java.util.List;
61
import java.util.logging.Level;
62
import java.util.logging.Logger;
64
import org.netbeans.api.debugger.jpda.CallStackFrame;
65
import org.netbeans.api.debugger.jpda.JPDAThread;
66
import org.netbeans.api.debugger.jpda.JPDAThreadGroup;
67
import org.netbeans.api.debugger.jpda.ObjectVariable;
68
import org.netbeans.modules.debugger.jpda.JPDADebuggerImpl;
69
import org.netbeans.spi.debugger.jpda.EditorContext.Operation;
70
import org.openide.ErrorManager;
71
import org.openide.util.NbBundle;
74
* The implementation of JPDAThread.
76
public final class JPDAThreadImpl implements JPDAThread {
79
* Suspended property of the thread. Fired when isSuspended() changes.
81
public static final String PROP_SUSPENDED = "suspended";
83
private ThreadReference threadReference;
84
private JPDADebuggerImpl debugger;
85
private boolean suspended;
86
private int suspendCount;
87
private Operation currentOperation;
88
private List<Operation> lastOperations;
89
private boolean doKeepLastOperations;
90
private ReturnVariableImpl returnVariable;
91
private PropertyChangeSupport pch = new PropertyChangeSupport(this);
92
private CallStackFrame[] cachedFrames;
93
private int cachedFramesFrom = -1;
94
private int cachedFramesTo = -1;
95
private Object cachedFramesLock = new Object();
97
public JPDAThreadImpl (
98
ThreadReference threadReference,
99
JPDADebuggerImpl debugger
101
this.threadReference = threadReference;
102
this.debugger = debugger;
103
suspended = threadReference.isSuspended();
104
suspendCount = threadReference.suspendCount();
108
* Getter for the name of thread property.
110
* @return name of thread.
112
public String getName () {
114
return threadReference.name ();
115
} catch (IllegalThreadStateException ex) {
116
return ""; // Thrown when thread has exited
117
} catch (ObjectCollectedException ex) {
119
} catch (VMDisconnectedException ex) {
125
* Returns parent thread group.
127
* @return parent thread group.
129
public JPDAThreadGroup getParentThreadGroup () {
131
ThreadGroupReference tgr = threadReference.threadGroup ();
132
if (tgr == null) return null;
133
return debugger.getThreadGroup(tgr);
134
} catch (IllegalThreadStateException ex) {
135
return null; // Thrown when thread has exited
136
} catch (ObjectCollectedException ex) {
138
} catch (VMDisconnectedException ex) {
144
* Returns line number of the location this thread stopped at.
145
* The thread should be suspended at the moment this method is called.
147
* @return line number of the current location if the thread is suspended,
148
* contains at least one frame and the topmost frame does not
149
* represent a native method invocation; <CODE>-1</CODE> otherwise
150
* @see CallStackFrame
152
public int getLineNumber (String stratum) {
154
if (threadReference.frameCount () < 1) return -1;
155
return threadReference.frame (0).location ().lineNumber (stratum);
156
} catch (ObjectCollectedException ex) {
157
} catch (InvalidStackFrameException ex) {
158
} catch (IncompatibleThreadStateException ex) {
159
} catch (IllegalThreadStateException ex) {
160
// Thrown when thread has exited
161
} catch (VMDisconnectedException ex) {
166
public synchronized Operation getCurrentOperation() {
167
return currentOperation;
170
public synchronized void setCurrentOperation(Operation operation) { // Set the current operation for the default stratum.
171
this.currentOperation = operation;
174
public synchronized List<Operation> getLastOperations() {
175
return lastOperations;
178
public synchronized void addLastOperation(Operation operation) {
179
if (lastOperations == null) {
180
lastOperations = new ArrayList<Operation>();
182
lastOperations.add(operation);
185
public synchronized void clearLastOperations() {
186
if (lastOperations != null) {
187
for (Operation last : lastOperations) {
188
last.setReturnValue(null); // reset the returned value.
189
// Operation might be reused, but the execution path is gone.
192
lastOperations = null;
195
public synchronized void holdLastOperations(boolean doHold) {
196
doKeepLastOperations = doHold;
201
* Returns current state of this thread.
203
* @return current state of this thread
205
public int getState () {
207
return threadReference.status ();
208
} catch (IllegalThreadStateException ex) {
209
// Thrown when thread has exited
210
} catch (ObjectCollectedException ex) {
211
} catch (VMDisconnectedException ex) {
213
return STATE_UNKNOWN;
217
* Returns true if this thread is suspended by debugger.
219
* @return true if this thread is suspended by debugger
221
public synchronized boolean isSuspended () {
226
* Returns true if this thread is suspended by debugger.
228
* @return true if this thread is suspended by debugger
230
public boolean isThreadSuspended () {
232
return threadReference.isSuspended ();
233
} catch (IllegalThreadStateException ex) {
234
// Thrown when thread has exited
235
} catch (ObjectCollectedException ex) {
236
} catch (VMDisconnectedException ex) {
242
* If this thread is suspended returns class name where this thread is stopped.
244
* @return class name where this thread is stopped.
246
public String getClassName () {
248
if (threadReference.frameCount () < 1) return "";
249
return threadReference.frame (0).location ().declaringType ().name ();
250
} catch (ObjectCollectedException ex) {
251
} catch (InvalidStackFrameException ex) {
252
} catch (IncompatibleThreadStateException ex) {
253
} catch (IllegalThreadStateException ex) {
254
// Thrown when thread has exited
255
} catch (VMDisconnectedException ex) {
261
* If this thread is suspended returns method name where this thread is stopped.
263
* @return method name where this thread is stopped.
265
public String getMethodName () {
267
if (threadReference.frameCount () < 1) return "";
268
return threadReference.frame (0).location ().method ().name ();
269
} catch (ObjectCollectedException ex) {
270
} catch (InvalidStackFrameException ex) {
271
} catch (IncompatibleThreadStateException ex) {
272
} catch (IllegalThreadStateException ex) {
273
// Thrown when thread has exited
274
} catch (VMDisconnectedException ex) {
280
* Returns name of file of this frame or null if thread has no frame.
282
* @return Returns name of file of this frame.
284
public String getSourceName (String stratum) throws AbsentInformationException {
286
if (threadReference.frameCount () < 1) return "";
287
return threadReference.frame (0).location ().sourceName (stratum);
288
} catch (ObjectCollectedException ex) {
289
} catch (InvalidStackFrameException ex) {
290
} catch (IncompatibleThreadStateException ex) {
291
} catch (IllegalThreadStateException ex) {
292
// Thrown when thread has exited
293
} catch (VMDisconnectedException ex) {
299
* Returns name of file of this frame or null if thread has no frame.
301
* @return Returns name of file of this frame.
303
public String getSourcePath (String stratum)
304
throws AbsentInformationException {
306
if (threadReference.frameCount () < 1) return "";
307
return threadReference.frame (0).location ().sourcePath (stratum);
308
} catch (ObjectCollectedException ex) {
309
} catch (InvalidStackFrameException ex) {
310
} catch (IncompatibleThreadStateException ex) {
311
} catch (IllegalThreadStateException ex) {
312
// Thrown when thread has exited
313
} catch (VMDisconnectedException ex) {
319
* Returns call stack for this thread.
321
* @throws AbsentInformationException if the thread is running or not able
322
* to return callstack. If the thread is in an incompatible state
323
* (e.g. running), the AbsentInformationException has
324
* IncompatibleThreadStateException as a cause.
325
* If the thread is collected, the AbsentInformationException has
326
* ObjectCollectedException as a cause.
329
public CallStackFrame[] getCallStack () throws AbsentInformationException {
330
return getCallStack (0, getStackDepth ());
333
private Object lastBottomSF;
336
* Returns call stack for this thread on the given indexes.
338
* @param from a from index, inclusive
339
* @param to a to index, exclusive
340
* @throws AbsentInformationException if the thread is running or not able
341
* to return callstack. If the thread is in an incompatible state
342
* (e.g. running), the AbsentInformationException has
343
* IncompatibleThreadStateException as a cause.
344
* If the thread is collected, the AbsentInformationException has
345
* ObjectCollectedException as a cause.
348
public CallStackFrame[] getCallStack (int from, int to)
349
throws AbsentInformationException {
351
int max = threadReference.frameCount();
352
from = Math.min(from, max);
353
to = Math.min(to, max);
355
synchronized (cachedFramesLock) {
356
if (from == cachedFramesFrom && to == cachedFramesTo) {
362
throw new IndexOutOfBoundsException("from = "+from);
365
return new CallStackFrame[0];
368
throw new IndexOutOfBoundsException("from = "+from+" is too high, frame count = "+max);
370
int length = to - from;
371
if (length < 0 || (from+length) > max) {
372
throw new IndexOutOfBoundsException("from = "+from+", to = "+to+", frame count = "+max);
374
List l = threadReference.frames (from, length);
376
CallStackFrame[] frames = new CallStackFrame[n];
377
for (int i = 0; i < n; i++) {
378
frames[i] = new CallStackFrameImpl((StackFrame) l.get(i), from + i, debugger);
379
if (from == 0 && i == 0 && currentOperation != null) {
380
((CallStackFrameImpl) frames[i]).setCurrentOperation(currentOperation);
384
synchronized (cachedFramesLock) {
385
cachedFrames = frames;
386
cachedFramesFrom = from;
391
} catch (IncompatibleThreadStateException ex) {
392
AbsentInformationException aiex = new AbsentInformationException(ex.getLocalizedMessage());
395
} catch (ObjectCollectedException ocex) {
396
AbsentInformationException aiex = new AbsentInformationException(ocex.getLocalizedMessage());
397
aiex.initCause(ocex);
399
} catch (IllegalThreadStateException itsex) {
400
// Thrown when thread has exited
401
AbsentInformationException aiex = new AbsentInformationException(itsex.getLocalizedMessage());
402
aiex.initCause(itsex);
404
} catch (VMDisconnectedException ex) {
405
return new CallStackFrame [0];
409
private void cleanCachedFrames() {
410
synchronized (cachedFramesLock) {
412
cachedFramesFrom = -1;
418
* Returns length of current call stack.
420
* @return length of current call stack
422
public int getStackDepth () {
424
return threadReference.frameCount ();
425
} catch (IllegalThreadStateException ex) {
426
// Thrown when thread has exited
427
} catch (ObjectCollectedException ex) {
428
} catch (IncompatibleThreadStateException e) {
433
public void popFrames(StackFrame sf) throws IncompatibleThreadStateException {
435
threadReference.popFrames(sf);
437
setReturnVariable(null); // Clear the return var
438
} catch (IllegalThreadStateException ex) {
439
throw new IncompatibleThreadStateException("Thread exited.");
440
} catch (ObjectCollectedException ex) {
441
throw new IncompatibleThreadStateException("Thread died.");
442
} catch (NativeMethodException nmex) {
444
ErrorManager.getDefault().notify(
445
ErrorManager.getDefault().annotate(nmex,
446
NbBundle.getMessage(JPDAThreadImpl.class, "MSG_NativeMethodPop")));
447
} catch (InternalException iex) {
449
if (iex.errorCode() == 32) {
450
ErrorManager.getDefault().notify(
451
ErrorManager.getDefault().annotate(iex,
452
NbBundle.getMessage(JPDAThreadImpl.class, "MSG_NativeMethodPop")));
462
public void suspend () {
463
Boolean suspendedToFire = null;
464
synchronized (this) {
466
if (!isSuspended ()) {
467
threadReference.suspend ();
468
suspendedToFire = Boolean.TRUE;
471
//System.err.println("suspend("+getName()+") suspended = true");
473
} catch (IllegalThreadStateException ex) {
474
// Thrown when thread has exited
475
} catch (ObjectCollectedException ex) {
476
} catch (VMDisconnectedException ex) {
479
if (suspendedToFire != null) {
480
pch.firePropertyChange(PROP_SUSPENDED,
481
Boolean.valueOf(!suspendedToFire.booleanValue()),
489
public void resume () {
490
if (this == debugger.getCurrentThread()) {
491
boolean can = debugger.currentThreadToBeResumed();
494
Boolean suspendedToFire = null;
495
synchronized (this) {
496
waitUntilMethodInvokeDone();
497
setReturnVariable(null); // Clear the return var on resume
498
setCurrentOperation(null);
499
if (!doKeepLastOperations) {
500
clearLastOperations();
503
if (isSuspended ()) {
504
int count = threadReference.suspendCount ();
506
threadReference.resume (); count--;
508
suspendedToFire = Boolean.FALSE;
511
//System.err.println("resume("+getName()+") suspended = false");
513
methodInvokingDisabledUntilResumed = false;
514
} catch (IllegalThreadStateException ex) {
515
// Thrown when thread has exited
516
} catch (ObjectCollectedException ex) {
517
} catch (VMDisconnectedException ex) {
521
if (suspendedToFire != null) {
522
pch.firePropertyChange(PROP_SUSPENDED,
523
Boolean.valueOf(!suspendedToFire.booleanValue()),
528
public void notifyToBeResumed() {
529
//System.err.println("notifyToBeResumed("+getName()+")");
530
notifyToBeRunning(true, true);
533
private void notifyToBeRunning(boolean clearVars, boolean resumed) {
534
Boolean suspendedToFire = null;
535
synchronized (this) {
537
waitUntilMethodInvokeDone();
539
//System.err.println("notifyToBeRunning("+getName()+"), resumed = "+resumed+", suspendCount = "+suspendCount+", thread's suspendCount = "+threadReference.suspendCount());
540
if (resumed && (--suspendCount > 0)) return ;
541
//System.err.println(" suspendCount = 0, var suspended = "+suspended);
544
setCurrentOperation(null);
545
setReturnVariable(null); // Clear the return var on resume
546
if (!doKeepLastOperations) {
547
clearLastOperations();
551
//System.err.println("notifyToBeRunning("+getName()+") suspended = false");
553
suspendedToFire = Boolean.FALSE;
554
methodInvokingDisabledUntilResumed = false;
558
if (suspendedToFire != null) {
559
pch.firePropertyChange(PROP_SUSPENDED,
560
Boolean.valueOf(!suspendedToFire.booleanValue()),
565
public void notifySuspended() {
566
Boolean suspendedToFire = null;
567
synchronized (this) {
569
suspendCount = threadReference.suspendCount();
570
} catch (IllegalThreadStateException ex) {
571
return ; // Thrown when thread has exited
572
} catch (ObjectCollectedException ocex) {
573
return ; // The thread is gone
575
//System.err.println("notifySuspended("+getName()+") suspendCount = "+suspendCount+", var suspended = "+suspended);
576
if (!suspended && isThreadSuspended()) {
577
//System.err.println(" setting suspended = true");
579
suspendedToFire = Boolean.TRUE;
582
if (suspendedToFire != null) {
583
pch.firePropertyChange(PROP_SUSPENDED,
584
Boolean.valueOf(!suspendedToFire.booleanValue()),
589
private boolean methodInvoking;
590
private boolean methodInvokingDisabledUntilResumed;
592
public void notifyMethodInvoking() throws PropertyVetoException {
593
synchronized (this) {
594
if (methodInvokingDisabledUntilResumed) {
595
throw new PropertyVetoException("disabled until resumed", null);
597
if (methodInvoking) {
598
throw new PropertyVetoException("Already invoking!", null);
600
methodInvoking = true;
602
notifyToBeRunning(false, false);
605
public void notifyMethodInvokeDone() {
606
synchronized (this) {
607
methodInvoking = false;
613
public synchronized boolean isMethodInvoking() {
614
return methodInvoking;
617
public void waitUntilMethodInvokeDone() {
618
synchronized (this) {
619
while (methodInvoking) {
622
} catch (InterruptedException iex) {
629
public synchronized void disableMethodInvokeUntilResumed() {
630
methodInvokingDisabledUntilResumed = true;
633
public void interrupt() {
635
if (isSuspended ()) return;
636
threadReference.interrupt();
637
} catch (IllegalThreadStateException ex) {
638
// Thrown when thread has exited
639
} catch (ObjectCollectedException ex) {
640
} catch (VMDisconnectedException ex) {
645
* Sets this thread current.
647
* @see JPDADebugger#getCurrentThread
649
public void makeCurrent () {
650
debugger.setCurrentThread (this);
654
* Returns monitor this thread is waiting on.
656
* @return monitor this thread is waiting on
658
public ObjectVariable getContendedMonitor () {
660
if (!threadReference.virtualMachine().canGetCurrentContendedMonitor()) {
663
} catch (IllegalThreadStateException ex) {
664
// Thrown when thread has exited
666
} catch (ObjectCollectedException ocex) {
670
synchronized (this) {
671
if (!isSuspended()) return null;
672
if ("DestroyJavaVM".equals(threadReference.name())) {
673
// See defect #6474293
677
or = threadReference.currentContendedMonitor ();
678
} catch (IllegalThreadStateException ex) {
679
// Thrown when thread has exited
681
} catch (ObjectCollectedException ex) {
683
} catch (IncompatibleThreadStateException e) {
684
String msg = "Thread '"+threadReference.name()+
685
"': status = "+threadReference.status()+
686
", is suspended = "+threadReference.isSuspended()+
687
", suspend count = "+threadReference.suspendCount()+
688
", is at breakpoint = "+threadReference.isAtBreakpoint()+
689
", internal suspend status = "+suspended;
690
Logger.getLogger(JPDAThreadImpl.class.getName()).log(Level.WARNING, msg, e);
692
} catch (com.sun.jdi.InternalException iex) {
693
String msg = "Thread '"+threadReference.name()+
694
"': status = "+threadReference.status()+
695
", is suspended = "+threadReference.isSuspended()+
696
", suspend count = "+threadReference.suspendCount()+
697
", is at breakpoint = "+threadReference.isAtBreakpoint()+
698
", internal suspend status = "+suspended;
699
Logger.getLogger(JPDAThreadImpl.class.getName()).log(Level.WARNING, msg, iex);
703
if (or == null) return null;
704
return new ThisVariable (debugger, or, "");
708
* Returns monitors owned by this thread.
710
* @return monitors owned by this thread
712
public ObjectVariable[] getOwnedMonitors () {
714
if (!threadReference.virtualMachine().canGetOwnedMonitorInfo()) {
715
return new ObjectVariable[0];
717
} catch (IllegalThreadStateException ex) {
718
// Thrown when thread has exited
719
return new ObjectVariable [0];
720
} catch (ObjectCollectedException ocex) {
721
return new ObjectVariable [0];
724
synchronized (this) {
725
if (!isSuspended()) return new ObjectVariable [0];
726
if ("DestroyJavaVM".equals(threadReference.name())) {
727
// See defect #6474293
728
return new ObjectVariable[0];
731
l = threadReference.ownedMonitors ();
732
} catch (IllegalThreadStateException ex) {
733
// Thrown when thread has exited
734
return new ObjectVariable [0];
735
} catch (ObjectCollectedException ex) {
736
return new ObjectVariable [0];
737
} catch (IncompatibleThreadStateException e) {
738
String msg = "Thread '"+threadReference.name()+
739
"': status = "+threadReference.status()+
740
", is suspended = "+threadReference.isSuspended()+
741
", suspend count = "+threadReference.suspendCount()+
742
", is at breakpoint = "+threadReference.isAtBreakpoint()+
743
", internal suspend status = "+suspended;
744
Logger.getLogger(JPDAThreadImpl.class.getName()).log(Level.WARNING, msg, e);
745
return new ObjectVariable [0];
746
} catch (com.sun.jdi.InternalException iex) {
747
String msg = "Thread '"+threadReference.name()+
748
"': status = "+threadReference.status()+
749
", is suspended = "+threadReference.isSuspended()+
750
", suspend count = "+threadReference.suspendCount()+
751
", is at breakpoint = "+threadReference.isAtBreakpoint()+
752
", internal suspend status = "+suspended;
753
Logger.getLogger(JPDAThreadImpl.class.getName()).log(Level.WARNING, msg, iex);
754
return new ObjectVariable [0];
757
int i, k = l.size ();
758
ObjectVariable[] vs = new ObjectVariable [k];
759
for (i = 0; i < k; i++) {
760
vs [i] = new ThisVariable (debugger, (ObjectReference) l.get (i), "");
765
public ThreadReference getThreadReference () {
766
return threadReference;
769
public synchronized ReturnVariableImpl getReturnVariable() {
770
return returnVariable;
773
public synchronized void setReturnVariable(ReturnVariableImpl returnVariable) {
774
this.returnVariable = returnVariable;
777
public void addPropertyChangeListener(PropertyChangeListener l) {
778
pch.addPropertyChangeListener(l);
781
public void removePropertyChangeListener(PropertyChangeListener l) {
782
pch.removePropertyChangeListener(l);
785
private void fireSuspended(boolean suspended) {
786
pch.firePropertyChange(PROP_SUSPENDED,
787
Boolean.valueOf(!suspended), Boolean.valueOf(suspended));