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