package bluej.debugger.jdi;
import java.util.*;
import bluej.Config;
import bluej.debugger.*;
import bluej.debugger.gentype.JavaType;
import bluej.utility.Debug;
import com.sun.jdi.*;
import com.sun.jdi.request.EventRequestManager;
import com.sun.jdi.request.StepRequest;
import threadchecker.OnThread;
import threadchecker.Tag;
| This class represents a thread running on the remote virtual machine.
|
| @author Michael Kolling
|
class JdiThread
extends DebuggerThread{
static final String statusFinished = Config.getString("debugger.threadstatus.finished");
static final String statusBreakpoint = Config.getString("debugger.threadstatus.breakpoint");
static final String statusStopped = Config.getString("debugger.threadstatus.stopped");
static final String statusMonitor = Config.getString("debugger.threadstatus.monitor");
static final String statusNotStarted = Config.getString("debugger.threadstatus.notstarted");
static final String statusRunning = Config.getString("debugger.threadstatus.running");
static final String statusSleeping = Config.getString("debugger.threadstatus.sleeping");
static final String statusUnknown = Config.getString("debugger.threadstatus.unknown");
static final String statusWaiting = Config.getString("debugger.threadstatus.waiting");
static final String statusZombie = Config.getString("debugger.threadstatus.zombie");
| a list of classes to exclude from source display
|
@OnThread(value = Tag.Any, requireSynchronized = true)
private static List<String> excludes;
@OnThread(Tag.Any)
private static synchronized List getExcludes()
{
if (excludes == null) {
setExcludes("java.*, javax.*, sun.*, com.sun.*");
}
return excludes;
}
@OnThread(Tag.Any)
private static synchronized void setExcludes(String excludeString)
{
StringTokenizer t = new StringTokenizer(excludeString, " ,;");
List<String> list = new ArrayList<String>();
while (t.hasMoreTokens()){
list.add(t.nextToken());
}
excludes = list;
}
@OnThread(Tag.Any)
p.public static void addExcludesToRequest(StepRequest request)
{
Iterator<String> iter = getExcludes().iterator();
while (iter.hasNext()){
String pattern = iter.next();
request.addClassExclusionFilter(pattern);
}
}
| the reference to the remote thread
|
@OnThread(Tag.Any)
private final ThreadReference rt;
| We track suspension status internally
|
@OnThread(value = Tag.Any,requireSynchronized = true)
private boolean isSuspended;
| Any active step request
|
@OnThread(Tag.Any)
StepRequest stepRequest;
|
| Note that we have to track suspension status internally, because JDI will happily
| tell us that a thread is suspended when, in fact, it is suspended only because of some
| VM event which suspends every thread (ThreadStart / ThreadDeath) or because of application
| suspension - see https://bugs.openjdk.java.net/browse/JDK-4257690
|
private int selectedFrame;
@OnThread(Tag.Any)
private EventRequestManager eventReqMgr;
@OnThread(Tag.Any)
private final JdiDebugger debugger;
@OnThread(Tag.Any)
public JdiThread(JdiDebugger debugger, ThreadReference rt)
{
this.rt = rt;
this.debugger = debugger;
selectedFrame = 0;
}
| Return the name of this thread.
|
public String getName()
{
String name = null;
try {
name = rt.name();
}
catch(Exception e) {
}
return name;
}
| Return the reference to the thread object in the remote machine.
|
@OnThread(Tag.Any)
ThreadReference getRemoteThread()
{
return rt;
}
| Return the current status of this thread.
|
public String getStatus()
{
try {
if (rt.isAtBreakpoint()) {
if (VMReference.isAtMainBreakpoint(rt)) {
return statusFinished;
}
else {
return statusBreakpoint;
}
}
if (rt.isSuspended()) {
return statusStopped;
}
int status = rt.status();
switch(status) {
case ThreadReference.THREAD_STATUS_MONITOR:
return statusMonitor;
case ThreadReference.THREAD_STATUS_NOT_STARTED:
return statusNotStarted;
case ThreadReference.THREAD_STATUS_RUNNING:
return statusRunning;
case ThreadReference.THREAD_STATUS_SLEEPING:
return statusSleeping;
case ThreadReference.THREAD_STATUS_UNKNOWN:
return statusUnknown;
case ThreadReference.THREAD_STATUS_WAIT:
return statusWaiting;
case ThreadReference.THREAD_STATUS_ZOMBIE:
return statusZombie;
}
}
catch(Exception e) {
return "???";
}
return null;
}
| Return true if this is a user thread that is in idle state
| (finished).
|
public boolean isFinished()
{
try {
return rt.isAtBreakpoint() && VMReference.isAtMainBreakpoint(rt);
}
catch (VMDisconnectedException vmde) {
return true;
}
}
| Return true if this thread is currently suspended.
|
@OnThread(Tag.Any)
public synchronized boolean isSuspended()
{
return isSuspended;
}
| Return true if this thread is currently at a breakpoint.
|
public boolean isAtBreakpoint()
{
return rt.isAtBreakpoint();
}
| Return the class this thread was executing in when the
| specified stack frame was active.
|
public String getClass(int frameNo)
{
try {
return rt.frame(frameNo).location().declaringType().name();
}
catch(Exception e) {
return "<error finding type at frame " + frameNo +">";
}
}
| Return the source name of the class this thread was
| executing in when the specified stack frame was active.
|
public String getClassSourceName(int frameNo)
{
try {
return rt.frame(frameNo).location().sourceName();
}
catch(Exception e) {
return "<no source at frame no " + frameNo +">";
}
}
| Return the line number in the source where this thread was
| executing when the specified stack frame was active.
|
public int getLineNumber(int frameNo)
{
try {
return rt.frame(frameNo).location().lineNumber();
}
catch(Exception e) {
return 1;
}
}
static final String MAIN_THREADGROUP = "main";
public boolean isKnownSystemThread()
{
try {
ThreadGroupReference tgr = rt.threadGroup();
if (tgr == null || ! tgr.name().equals(MAIN_THREADGROUP)) {
return true;
}
String name = rt.name();
if (name.startsWith("AWT-Event")
|| name.equals("JavaFX Application Thread")
|| name.startsWith("WindowsNative"))
{
return false;
}
if (name.startsWith("AWT-") ||
name.equals("DestroyJavaVM") ||
name.equals("BlueJ worker thread") ||
name.equals("Timer Queue") ||
name.equals("Screen Updater") ||
name.startsWith("SunToolkit.") ||
name.startsWith("Native Carbon") ||
name.equals("JavaFX-Launcher") ||
name.startsWith("QuantumRenderer") ||
name.equals("JavaFX BlueJ Helper") ||
name.equals("Java2D Disposer") ||
name.equals("InvokeLaterDispatcher"))
{
return true;
}
return false;
}
catch (VMDisconnectedException vmde) {
return false;
}
catch (ObjectCollectedException oce) {
return true;
}
}
| Get strings showing the current stack frames. Ignore everything
| including the __SHELL class and below.
|
| <p>The thread must be suspended to do this. Otherwise an empty list
| is returned.
|
| @return A List of SourceLocations
|
@OnThread(Tag.Any)
public List getStack()
{
return getStack(rt);
}
| Get strings showing the current stack frames. Ignore everything
| including the __SHELL class and below.
|
| <p>The thread must be suspended to do this. Otherwise an empty list
| is returned.
|
| @return A List of SourceLocations
|
@OnThread(Tag.Any)
public static List getStack(ThreadReference thr)
{
try {
if (thr.isSuspended()) {
List<SourceLocation> stack = new ArrayList<SourceLocation>();
List<StackFrame> frames = thr.frames();
for (int i = 0; i < frames.size(); i++) {
StackFrame f = (StackFrame)frames.get(i);
Location loc = f.location();
String className = loc.declaringType().name();
String fileName = null;
try {
fileName = loc.sourceName();
}
catch(AbsentInformationException e) {
}
String methodName = loc.method().name();
int lineNumber = loc.lineNumber();
stack.add(new SourceLocation(className, fileName,
methodName, lineNumber));
}
return stack;
}
}
catch (VMDisconnectedException vmde) {
}
catch(IncompatibleThreadStateException e) {
}
catch(InvalidStackFrameException isfe) {
}
return new ArrayList<SourceLocation>();
}
| Return strings listing the local variables.
|
| The thread must be suspended to do this. Otherwise an empty List
| is returned.
|
@OnThread(Tag.Any)
@SuppressWarnings("threadchecker")
public List getLocalVariables(int frameNo)
{
try {
if (rt.isSuspended()) {
StackFrame frame = rt.frame(frameNo);
List<LocalVariable> vars = frame.visibleVariables();
List<VarDisplayInfo> localVars = new ArrayList<>();
List<String> localVals = new ArrayList<String>();
List<Type> localTypes = new ArrayList<Type>();
List<String> genericSigs = new ArrayList<String>();
List<String> typeNames = new ArrayList<String>();
ReferenceType declaringType = frame.location().declaringType();
for (int i = 0; i < vars.size(); i++) {
LocalVariable var = vars.get(i);
String val = JdiUtils.getJdiUtils().getValueString(frame.getValue(var));
localVals.add(val);
try {
localTypes.add(var.type());
}
catch (ClassNotLoadedException cnle) {
localTypes.add(null);
}
genericSigs.add(var.genericSignature());
typeNames.add(var.typeName());
}
for (int i = 0; i < vars.size(); i++) {
LocalVariable var = vars.get(i);
JavaType vartype = JdiReflective.fromLocalVar(localTypes.get(i), genericSigs.get(i),
typeNames.get(i), declaringType);
int iFinal = i;
localVars.add(new VarDisplayInfo(vartype, var, localVals.get(i),
varIsObject(frameNo, i) ?
() -> getStackObject(frameNo, iFinal)
: null
));
}
return localVars;
}
}
catch (IncompatibleThreadStateException itse) {
}
catch (AbsentInformationException ase) {
}
catch (VMDisconnectedException vmde) {
}
catch (InvalidStackFrameException e) {
try {
Thread.sleep(100);
return getLocalVariables(frameNo);
}
catch (InterruptedException ie) {
}
}
return new ArrayList<>();
}
| Return true if the identified slot on the stack contains an object.
|
public boolean varIsObject(int frameNo, int index)
{
try
{
if (rt.isSuspended())
{
StackFrame frame = rt.frame(frameNo);
List<LocalVariable> vars = frame.visibleVariables();
if (index >= vars.size())
{
return false;
}
LocalVariable var = vars.get(index);
Value val = frame.getValue(var);
return (val instanceof ObjectReference);
}
else
{
return false;
}
}
catch (IncompatibleThreadStateException | InvalidStackFrameException itse)
{
}
catch(Exception e)
{
Debug.reportError("could not get local variable info: " + e);
e.printStackTrace(System.out);
}
return false;
}
| Return an object from this thread's stack. The variable must contain
| an object.
|
@OnThread(Tag.Any)
@SuppressWarnings("threadchecker")
public DebuggerObject getStackObject(int frameNo, int index)
{
try {
if (rt.isSuspended()) {
StackFrame frame = rt.frame(frameNo);
List<LocalVariable> vars = frame.visibleVariables();
LocalVariable var = vars.get(index);
JavaType vartype = JdiReflective.fromLocalVar(frame, var);
ObjectReference val = (ObjectReference)frame.getValue(var);
return JdiObject.getDebuggerObject(val, vartype);
}
else{ return null;
}
}
catch(Exception e) {
Debug.reportError("could not get local variable info: " + e);
e.printStackTrace(System.out);
}
return null;
}
@Override
@OnThread(Tag.Any)
public DebuggerObject getCurrentObject(int frameNo)
{
try {
if (rt.isSuspended()) {
StackFrame frame = rt.frame(frameNo);
return JdiObject.getDebuggerObject(frame.thisObject());
}
}
catch (IncompatibleThreadStateException e) {
}
catch (VMDisconnectedException vmde) {
}
catch (InvalidStackFrameException ise) {
}
return JdiObject.getDebuggerObject(null);
}
@Override
public DebuggerClass getCurrentClass(int frameNo)
{
try {
if (rt.isSuspended()) {
StackFrame frame = rt.frame(frameNo);
return new JdiClass(frame.location().declaringType());
}
}
catch (InvalidStackFrameException isfe) {
}
catch (IncompatibleThreadStateException e) {
}
catch (VMDisconnectedException vmde) {
}
return null;
}
| Specify a frame as the currently selected frame in this thread.
|
public void setSelectedFrame(int frame)
{
selectedFrame = frame;
}
| Return the selected frame in this thread.
|
public int getSelectedFrame()
{
return selectedFrame;
}
| Halt this thread.
|
@OnThread(Tag.Any)
public synchronized void halt()
{
try {
if (! isSuspended) {
rt.suspend();
debugger.emitThreadHaltEvent(this);
isSuspended = true;
}
}
catch (VMDisconnectedException vmde) {
}
}
| Continue a previously halted thread.
|
@OnThread(Tag.Any)
public synchronized void cont()
{
try {
if (isSuspended) {
debugger.emitThreadResumedEvent(this);
rt.resume();
isSuspended = false;
}
}
catch (VMDisconnectedException vmde) {
}
}
| Inform the JdiThread that the underlying thread has been suspended due to
| (for example) hitting a breakpoint.
|
@OnThread(Tag.Any)
public void stopped()
{
synchronized (this)
{
isSuspended = true;
}
clearPreviousStep(getRemoteThread());
}
| Make this thread step a single line.
|
public void step()
{
boolean doStepOver = true;
try {
Location loc = rt.frame(0).location();
doStepOver = (loc.codeIndex() != -1);
}
catch (IncompatibleThreadStateException itse) {
}
doStep(doStepOver ? StepRequest.STEP_OVER : StepRequest.STEP_OUT);
}
@OnThread(Tag.Any)
public void stepInto()
{
doStep(StepRequest.STEP_INTO);
}
| Return the JDI ThreadReference which this JdiThread wraps.
|
@OnThread(Tag.Any)
public ThreadReference getThreadReference()
{
return rt;
}
@OnThread(Tag.Any)
private void doStep(int depth)
{
clearPreviousStep(rt);
stepRequest = eventReqMgr.createStepRequest(rt,
StepRequest.STEP_LINE, depth);
addExcludesToRequest(stepRequest);
stepRequest.addCountFilter(1);
stepRequest.putProperty(VMEventHandler.DONT_RESUME, "yes");
stepRequest.enable();
synchronized (this) {
if (isSuspended) {
debugger.emitThreadResumedEvent(this);
rt.resume();
isSuspended = false;
}
}
}
| A previously set step may not have completed yet - find out and
| if it is so, remove it.
|
@OnThread(Tag.Any)
private void clearPreviousStep(ThreadReference thread)
{
if (eventReqMgr == null)
getEventRequestManager();
if (stepRequest != null) {
eventReqMgr.deleteEventRequest(stepRequest);
stepRequest = null;
}
}
@OnThread(Tag.Any)
private void getEventRequestManager()
{
eventReqMgr = rt.virtualMachine().eventRequestManager();
}
public String toString()
{
try {
return getName() + " (" + getStatus() + ")";
}
catch (ObjectCollectedException oce)
{
return "collected";
}
}
@OnThread(Tag.Any)
public boolean sameThread(DebuggerThread dt)
{
if (dt != null && dt instanceof JdiThread) {
return getThreadReference().uniqueID() == ((JdiThread)dt).getThreadReference().uniqueID();
}
else {
return false;
}
}
| Called when we are the serverThread, to let us know we've been resumed
| (and should update our internal status accordingly)
|
@OnThread(Tag.Any)
public synchronized void notifyResumed()
{
isSuspended = false;
}
}
top,
use,
map,
class JdiThread
. getExcludes
. setExcludes
. addExcludesToRequest
. JdiThread
. getName
. getStatus
. isFinished
. isSuspended
. isAtBreakpoint
. getClass
. getClassSourceName
. getLineNumber
. isKnownSystemThread
. getStack
. getStack
. getLocalVariables
. varIsObject
. getStackObject
. getCurrentObject
. getCurrentClass
. setSelectedFrame
. getSelectedFrame
. halt
. cont
. stopped
. step
. stepInto
. getThreadReference
. doStep
. clearPreviousStep
. getEventRequestManager
. toString
. sameThread
. notifyResumed
861 neLoCode
+ 52 LoComm