diff --git a/debug/org.eclipse.cdt.debug.core/src/org/eclipse/cdt/debug/core/IState.java b/debug/org.eclipse.cdt.debug.core/src/org/eclipse/cdt/debug/core/IState.java index 1af1a37ae5f..a90b8f20714 100644 --- a/debug/org.eclipse.cdt.debug.core/src/org/eclipse/cdt/debug/core/IState.java +++ b/debug/org.eclipse.cdt.debug.core/src/org/eclipse/cdt/debug/core/IState.java @@ -6,6 +6,8 @@ package org.eclipse.cdt.debug.core; +import org.eclipse.core.runtime.IAdaptable; + /** * * Represents the current state of debug element. @@ -27,5 +29,17 @@ public interface IState public static final int TERMINATED = 10; public static final int CORE_DUMP_FILE = 11; - int getCurrentState(); + /** + * Returns the identifier of the current state. + * + * @return the identifier of the current state + */ + int getCurrentStateId(); + + /** + * Returns the info object associated with the current state. + * + * @return the info object associated with the current state + */ + Object getCurrentStateInfo(); } diff --git a/debug/org.eclipse.cdt.debug.core/src/org/eclipse/cdt/debug/internal/core/CDebugConfiguration.java b/debug/org.eclipse.cdt.debug.core/src/org/eclipse/cdt/debug/internal/core/CDebugConfiguration.java new file mode 100644 index 00000000000..9da1ab25c9b --- /dev/null +++ b/debug/org.eclipse.cdt.debug.core/src/org/eclipse/cdt/debug/internal/core/CDebugConfiguration.java @@ -0,0 +1,149 @@ +/* + *(c) Copyright QNX Software Systems Ltd. 2002. + * All Rights Reserved. + * + */ + +package org.eclipse.cdt.debug.internal.core; + +import org.eclipse.cdt.debug.core.cdi.ICSession; + +/** + * + * Provides the convenience access methods to the configuration + * parameters of the debug session. + * + * @since Aug 6, 2002 + */ +public class CDebugConfiguration +{ + private ICSession fSession; + + /** + * Constructor for CDebugConfiguration. + */ + public CDebugConfiguration( ICSession session ) + { + fSession = session; + } + + /** + * Returns whether this session supports termination. + * + * @return whether this session supports termination + */ + public boolean supportsTerminate() + { + return true; + } + + /** + * Returns whether this session supports disconnecting. + * + * @return whether this session supports disconnecting + */ + public boolean supportsDisconnect() + { + return true; + } + + /** + * Returns whether this session supports suspend/resume. + * + * @return whether this session supports suspend/resume + */ + public boolean supportsSuspendResume() + { + return true; + } + + /** + * Returns whether this session supports restarting. + * + * @return whether this session supports restarting + */ + public boolean supportsRestart() + { + return true; + } + + /** + * Returns whether this session supports stepping. + * + * @return whether this session supports stepping + */ + public boolean supportsStepping() + { + return true; + } + + /** + * Returns whether this session supports instruction stepping. + * + * @return whether this session supports instruction stepping + */ + public boolean supportsInstructionStepping() + { + return true; + } + + /** + * Returns whether this session supports breakpoints. + * + * @return whether this session supports breakpoints + */ + public boolean supportsBreakpoints() + { + return true; + } + + /** + * Returns whether this session supports registers. + * + * @return whether this session supports registers + */ + public boolean supportsRegisters() + { + return true; + } + + /** + * Returns whether this session supports register modification. + * + * @return whether this session supports registers modification + */ + public boolean supportsRegisterModification() + { + return true; + } + + /** + * Returns whether this session supports memory retrieval. + * + * @return whether this session supports memory retrieval + */ + public boolean supportsMemoryRetrieval() + { + return true; + } + + /** + * Returns whether this session supports memory modification. + * + * @return whether this session supports memory modification + */ + public boolean supportsMemoryModification() + { + return true; + } + + /** + * Returns whether this session supports expression evaluation. + * + * @return whether this session supports expression evaluation + */ + public boolean supportsExpressionEvaluation() + { + return true; + } +} diff --git a/debug/org.eclipse.cdt.debug.core/src/org/eclipse/cdt/debug/internal/core/CDebugTarget.java b/debug/org.eclipse.cdt.debug.core/src/org/eclipse/cdt/debug/internal/core/CDebugTarget.java index 158ce93871f..56777283cf7 100644 --- a/debug/org.eclipse.cdt.debug.core/src/org/eclipse/cdt/debug/internal/core/CDebugTarget.java +++ b/debug/org.eclipse.cdt.debug.core/src/org/eclipse/cdt/debug/internal/core/CDebugTarget.java @@ -7,6 +7,7 @@ package org.eclipse.cdt.debug.internal.core; import java.text.MessageFormat; import java.util.ArrayList; +import java.util.Iterator; import java.util.List; import org.eclipse.cdt.debug.core.CDebugModel; @@ -46,6 +47,7 @@ import org.eclipse.debug.core.model.IBreakpoint; import org.eclipse.debug.core.model.IDebugTarget; import org.eclipse.debug.core.model.IMemoryBlock; import org.eclipse.debug.core.model.IProcess; +import org.eclipse.debug.core.model.IStep; import org.eclipse.debug.core.model.IThread; /** @@ -58,7 +60,6 @@ public class CDebugTarget extends CDebugElement implements IDebugTarget, ICEventListener, IRestart, - IInstructionStep, IFormattedMemoryRetrieval, IState, ILaunchListener @@ -76,7 +77,7 @@ public class CDebugTarget extends CDebugElement private IProcess fProcess; /** - * Underlying CDI target. + * The underlying CDI target. */ private ICTarget fCDITarget; @@ -115,6 +116,11 @@ public class CDebugTarget extends CDebugElement */ private ILaunch fLaunch; + /** + * The debug configuration of this session + */ + private CDebugConfiguration fConfig; + /** * Whether terminate is supported. */ @@ -125,6 +131,16 @@ public class CDebugTarget extends CDebugElement */ private boolean fSupportsDisconnect; + /** + * The current state identifier. + */ + private int fCurrentStateId = IState.UNKNOWN; + + /** + * The current state info. + */ + private Object fCurrentStateInfo = null; + /** * Constructor for CDebugTarget. * @param target @@ -143,6 +159,9 @@ public class CDebugTarget extends CDebugElement setName( name ); setProcess( process ); setCDITarget( cdiTarget ); + fConfig = new CDebugConfiguration( cdiTarget.getSession() ); + fSupportsTerminate = allowsTerminate & fConfig.supportsTerminate(); + fSupportsDisconnect = allowsDisconnect & fConfig.supportsDisconnect(); setThreadList( new ArrayList( 5 ) ); initialize(); DebugPlugin.getDefault().getLaunchManager().addLaunchListener( this ); @@ -299,7 +318,7 @@ public class CDebugTarget extends CDebugElement */ public boolean canTerminate() { - return false; + return supportsTerminate() && isAvailable(); } /* (non-Javadoc) @@ -370,6 +389,23 @@ public class CDebugTarget extends CDebugElement */ public void resume() throws DebugException { + if ( !isSuspended() ) + { + return; + } + try + { + setSuspended( false ); + getCDITarget().resume(); + resumeThreads(); + fireResumeEvent( DebugEvent.CLIENT_REQUEST ); + } + catch( CDIException e ) + { + setSuspended( true ); + fireSuspendEvent( DebugEvent.CLIENT_REQUEST ); + targetRequestFailed( MessageFormat.format( "{0} occurred resuming target.", new String[] { e.toString() } ), e ); + } } /* (non-Javadoc) @@ -404,6 +440,13 @@ public class CDebugTarget extends CDebugElement { } + /** + * Notifies threads that they have been resumed + */ + protected void resumeThreads() + { + } + /* (non-Javadoc) * @see org.eclipse.debug.core.IBreakpointListener#breakpointAdded(IBreakpoint) */ @@ -425,12 +468,32 @@ public class CDebugTarget extends CDebugElement { } + /** + * Returns whether this debug target supports disconnecting. + * + * @return whether this debug target supports disconnecting + */ + protected boolean supportsDisconnect() + { + return fConfig.supportsDisconnect(); + } + + /** + * Returns whether this debug target supports termination. + * + * @return whether this debug target supports termination + */ + protected boolean supportsTerminate() + { + return fConfig.supportsTerminate(); + } + /* (non-Javadoc) * @see org.eclipse.debug.core.model.IDisconnect#canDisconnect() */ public boolean canDisconnect() { - return false; + return supportsDisconnect() && !isDisconnected(); } /* (non-Javadoc) @@ -438,6 +501,26 @@ public class CDebugTarget extends CDebugElement */ public void disconnect() throws DebugException { + if ( isDisconnected() ) + { + // already done + return; + } + + if ( !canDisconnect() ) + { + notSupported( "Session does not support \'disconnect\'" ); + } + + try + { + getCDITarget().disconnect(); + disconnected(); + } + catch( CDIException e ) + { + targetRequestFailed( MessageFormat.format( "{0} ocurred disconnecting from target.", new String[] { e.toString()} ), e ); + } } /* (non-Javadoc) @@ -530,6 +613,8 @@ public class CDebugTarget extends CDebugElement return this; if ( adapter.equals( ICTarget.class ) ) return fCDITarget; + if ( adapter.equals( IState.class ) ) + return this; return super.getAdapter( adapter ); } @@ -623,7 +708,7 @@ public class CDebugTarget extends CDebugElement */ public boolean canRestart() { - return false; + return fConfig.supportsRestart() && isSuspended() && isAvailable(); } /* (non-Javadoc) @@ -631,35 +716,27 @@ public class CDebugTarget extends CDebugElement */ public void restart() throws DebugException { + if ( !canRestart() ) + { + return; + } + + try + { + getCDITarget().restart(); + restarted(); + } + catch( CDIException e ) + { + targetRequestFailed( MessageFormat.format( "{0} ocurred restarting the target.", new String[] { e.toString()} ), e ); + } } - /* (non-Javadoc) - * @see org.eclipse.cdt.debug.core.IInstructionStep#canStepIntoInstruction() + /** + * Updates the state of this target for restarting. + * */ - public boolean canStepIntoInstruction() - { - return false; - } - - /* (non-Javadoc) - * @see org.eclipse.cdt.debug.core.IInstructionStep#canStepOverInstruction() - */ - public boolean canStepOverInstruction() - { - return false; - } - - /* (non-Javadoc) - * @see org.eclipse.cdt.debug.core.IInstructionStep#stepIntoInstruction() - */ - public void stepIntoInstruction() throws DebugException - { - } - - /* (non-Javadoc) - * @see org.eclipse.cdt.debug.core.IInstructionStep#stepOverInstruction() - */ - public void stepOverInstruction() throws DebugException + protected void restarted() { } @@ -704,7 +781,7 @@ public class CDebugTarget extends CDebugElement */ public boolean isAvailable() { - return false; + return !( isTerminated() || isTerminating() || isDisconnected() ); } /** @@ -754,8 +831,8 @@ public class CDebugTarget extends CDebugElement } /** - * Updates the state of this target for disconnection - * from the VM. + * Updates the state of this target for disconnection. + * */ protected void disconnected() { @@ -788,6 +865,13 @@ public class CDebugTarget extends CDebugElement */ protected void removeAllThreads() { + Iterator itr = getThreadList().iterator(); + setThreadList( new ArrayList( 0 ) ); + while( itr.hasNext() ) + { + CThread thread = (CThread)itr.next(); + thread.terminated(); + } } /** @@ -863,7 +947,9 @@ public class CDebugTarget extends CDebugElement private void handleSuspendedEvent( ICSuspendedEvent event ) { setSuspended( true ); + setCurrentStateId( IState.SUSPENDED ); ICSessionObject reason = event.getReason(); + setCurrentStateInfo( reason ); if ( reason instanceof ICEndSteppingRange ) { handleEndSteppingRange( (ICEndSteppingRange)reason ); @@ -881,6 +967,8 @@ public class CDebugTarget extends CDebugElement private void handleResumedEvent( ICResumedEvent event ) { setSuspended( false ); + setCurrentStateId( IState.RUNNING ); + setCurrentStateInfo( null ); fireResumeEvent( DebugEvent.UNSPECIFIED ); } @@ -901,16 +989,22 @@ public class CDebugTarget extends CDebugElement private void handleExitedEvent( ICExitedEvent event ) { + setCurrentStateId( IState.EXITED ); + setCurrentStateInfo( event.getExitInfo() ); fireChangeEvent( DebugEvent.STATE ); } private void handleTerminatedEvent( ICTerminatedEvent event ) { + setCurrentStateId( IState.TERMINATED ); + setCurrentStateInfo( null ); terminated(); } private void handleDisconnectedEvent( ICDisconnectedEvent event ) { + setCurrentStateId( IState.DISCONNECTED ); + setCurrentStateInfo( null ); disconnected(); } @@ -928,6 +1022,8 @@ public class CDebugTarget extends CDebugElement private void handleSteppingEvent( ICSteppingEvent event ) { + setCurrentStateId( IState.STEPPING ); + setCurrentStateInfo( null ); } private void handleThreadCreatedEvent( ICCreatedEvent event ) @@ -976,10 +1072,38 @@ public class CDebugTarget extends CDebugElement } /* (non-Javadoc) - * @see org.eclipse.cdt.debug.core.IState#getCurrentState() + * @see org.eclipse.cdt.debug.core.IState#getCurrentStateId() */ - public int getCurrentState() + public int getCurrentStateId() { - return IState.UNKNOWN; + return fCurrentStateId; + } + + /** + * Sets the current state identifier. + * + * @param id the identifier + */ + private void setCurrentStateId( int id ) + { + fCurrentStateId = id; + } + + /* (non-Javadoc) + * @see org.eclipse.cdt.debug.core.IState#getCurrentStateInfo() + */ + public Object getCurrentStateInfo() + { + return fCurrentStateInfo; + } + + /** + * Sets the info object of the current state. + * + * @param id the info object + */ + private void setCurrentStateInfo( Object info ) + { + fCurrentStateInfo = info; } } diff --git a/debug/org.eclipse.cdt.debug.core/src/org/eclipse/cdt/debug/internal/core/CLocalVariable.java b/debug/org.eclipse.cdt.debug.core/src/org/eclipse/cdt/debug/internal/core/CLocalVariable.java new file mode 100644 index 00000000000..88b5eab02dc --- /dev/null +++ b/debug/org.eclipse.cdt.debug.core/src/org/eclipse/cdt/debug/internal/core/CLocalVariable.java @@ -0,0 +1,112 @@ +/* + *(c) Copyright QNX Software Systems Ltd. 2002. + * All Rights Reserved. + * + */ + +package org.eclipse.cdt.debug.internal.core; + +import org.eclipse.cdt.debug.core.cdi.event.ICEvent; +import org.eclipse.cdt.debug.core.cdi.event.ICEventListener; +import org.eclipse.cdt.debug.core.cdi.model.ICVariable; +import org.eclipse.debug.core.DebugException; +import org.eclipse.debug.core.model.IValue; +import org.eclipse.debug.core.model.IVariable; + +/** + * + * Proxy to a local variaable on the target. + * + * @since Aug 7, 2002 + */ +public class CLocalVariable extends CDebugElement + implements IVariable, + ICEventListener +{ + + /** + * Constructor for CLocalVariable. + * @param target + */ + public CLocalVariable( CStackFrame stackFrame, ICVariable cdiVariable ) + { + super( (CDebugTarget)stackFrame.getDebugTarget() ); + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.IVariable#getValue() + */ + public IValue getValue() throws DebugException + { + return null; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.IVariable#getName() + */ + public String getName() throws DebugException + { + return null; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.IVariable#getReferenceTypeName() + */ + public String getReferenceTypeName() throws DebugException + { + return null; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.IVariable#hasValueChanged() + */ + public boolean hasValueChanged() throws DebugException + { + return false; + } + + /* (non-Javadoc) + * @see org.eclipse.cdt.debug.core.cdi.event.ICEventListener#handleDebugEvent(ICEvent) + */ + public void handleDebugEvent( ICEvent event ) + { + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.IValueModification#setValue(String) + */ + public void setValue( String expression ) throws DebugException + { + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.IValueModification#setValue(IValue) + */ + public void setValue( IValue value ) throws DebugException + { + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.IValueModification#supportsValueModification() + */ + public boolean supportsValueModification() + { + return false; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.IValueModification#verifyValue(String) + */ + public boolean verifyValue( String expression ) throws DebugException + { + return false; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.IValueModification#verifyValue(IValue) + */ + public boolean verifyValue( IValue value ) throws DebugException + { + return false; + } +} diff --git a/debug/org.eclipse.cdt.debug.core/src/org/eclipse/cdt/debug/internal/core/CStackFrame.java b/debug/org.eclipse.cdt.debug.core/src/org/eclipse/cdt/debug/internal/core/CStackFrame.java new file mode 100644 index 00000000000..20094c8decd --- /dev/null +++ b/debug/org.eclipse.cdt.debug.core/src/org/eclipse/cdt/debug/internal/core/CStackFrame.java @@ -0,0 +1,405 @@ +/* + *(c) Copyright QNX Software Systems Ltd. 2002. + * All Rights Reserved. + * + */ +package org.eclipse.cdt.debug.internal.core; + +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.eclipse.cdt.debug.core.cdi.CDIException; +import org.eclipse.cdt.debug.core.cdi.event.ICEvent; +import org.eclipse.cdt.debug.core.cdi.event.ICEventListener; +import org.eclipse.cdt.debug.core.cdi.model.ICArgument; +import org.eclipse.cdt.debug.core.cdi.model.ICStackFrame; +import org.eclipse.cdt.debug.core.cdi.model.ICVariable; +import org.eclipse.debug.core.DebugException; +import org.eclipse.debug.core.model.IRegisterGroup; +import org.eclipse.debug.core.model.IStackFrame; +import org.eclipse.debug.core.model.IThread; +import org.eclipse.debug.core.model.IVariable; + +/** + * + * Proxy to a stack frame on the target. + * + * @since Aug 7, 2002 + */ +public class CStackFrame extends CDebugElement + implements IStackFrame, + ICEventListener +{ + /** + * Underlying CDI stack frame. + */ + private ICStackFrame fCDIStackFrame; + + /** + * Containing thread. + */ + private CThread fThread; + + /** + * Visible variables. + */ + private List fVariables; + + /** + * Whether the variables need refreshing + */ + private boolean fRefreshVariables = true; + + /** + * Constructor for CStackFrame. + * @param target + */ + public CStackFrame( CThread thread, ICStackFrame cdiFrame ) + { + super( (CDebugTarget)thread.getDebugTarget() ); + fCDIStackFrame = cdiFrame; + setThread( thread ); + getCDISession().getEventManager().addEventListener( this ); + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.IStackFrame#getThread() + */ + public IThread getThread() + { + return fThread; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.IStackFrame#getVariables() + */ + public IVariable[] getVariables() throws DebugException + { + List list = getVariables0(); + return (IVariable[])list.toArray( new IVariable[list.size()] ); + } + + protected synchronized List getVariables0() throws DebugException + { + if ( fVariables == null ) + { + fVariables = new ArrayList(); + fVariables.addAll( getCDIArguments() ); + fVariables.addAll( getCDILocalVariables() ); + } + else if ( fRefreshVariables ) + { + updateVariables(); + } + fRefreshVariables = false; + return fVariables; + } + + /** + * Incrementally updates this stack frames variables. + * + */ + protected void updateVariables() throws DebugException + { + } + + /** + * Sets the containing thread. + * + * @param thread the containing thread + */ + protected void setThread( CThread thread ) + { + fThread = thread; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.IStackFrame#hasVariables() + */ + public boolean hasVariables() throws DebugException + { + return getVariables0().size() > 0; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.IStackFrame#getLineNumber() + */ + public int getLineNumber() throws DebugException + { + return 0; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.IStackFrame#getCharStart() + */ + public int getCharStart() throws DebugException + { + return 0; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.IStackFrame#getCharEnd() + */ + public int getCharEnd() throws DebugException + { + return 0; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.IStackFrame#getName() + */ + public String getName() throws DebugException + { + return null; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.IStackFrame#getRegisterGroups() + */ + public IRegisterGroup[] getRegisterGroups() throws DebugException + { + return null; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.IStackFrame#hasRegisterGroups() + */ + public boolean hasRegisterGroups() throws DebugException + { + return false; + } + + /* (non-Javadoc) + * @see org.eclipse.cdt.debug.core.cdi.event.ICEventListener#handleDebugEvent(ICEvent) + */ + public void handleDebugEvent(ICEvent event) + { + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.IStep#canStepInto() + */ + public boolean canStepInto() + { + return false; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.IStep#canStepOver() + */ + public boolean canStepOver() + { + return false; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.IStep#canStepReturn() + */ + public boolean canStepReturn() + { + return false; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.IStep#isStepping() + */ + public boolean isStepping() + { + return false; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.IStep#stepInto() + */ + public void stepInto() throws DebugException + { + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.IStep#stepOver() + */ + public void stepOver() throws DebugException + { + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.IStep#stepReturn() + */ + public void stepReturn() throws DebugException + { + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.ISuspendResume#canResume() + */ + public boolean canResume() + { + return false; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.ISuspendResume#canSuspend() + */ + public boolean canSuspend() + { + return false; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.ISuspendResume#isSuspended() + */ + public boolean isSuspended() + { + return false; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.ISuspendResume#resume() + */ + public void resume() throws DebugException + { + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.ISuspendResume#suspend() + */ + public void suspend() throws DebugException + { + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.ITerminate#canTerminate() + */ + public boolean canTerminate() + { + return false; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.ITerminate#isTerminated() + */ + public boolean isTerminated() + { + return false; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.model.ITerminate#terminate() + */ + public void terminate() throws DebugException + { + } + + /** + * Returns the underlying CDI stack frame that this model object is + * a proxy to. + * + * @return the underlying CDI stack frame + */ + protected ICStackFrame getCDIStackFrame() + { + return fCDIStackFrame; + } + + /** + * Sets the underlying CDI stack frame. Called by a thread + * when incrementally updating after a step has completed. + * + * @param frame the underlying stack frame + */ + protected void setCDIStackFrame( ICStackFrame frame ) + { + } + + /** + * The underlying stack frame that existed before the current underlying + * stack frame. Used only so that equality can be checked on stack frame + * after the new one has been set. + */ + protected ICStackFrame getLastCDIStackFrame() + { + return null; + } + + /** + * Helper method for computeStackFrames(). For the purposes of detecting if + * an underlying stack frame needs to be disposed, stack frames are equal if + * the frames are equal and the locations are equal. + */ + protected static boolean equalFrame( ICStackFrame frameOne, ICStackFrame frameTwo ) + { + return false; + } + + protected boolean exists() throws DebugException + { + return ((CThread)getThread()).computeStackFrames().indexOf( this ) != -1; + } + + /** + * @see IAdaptable#getAdapter(Class) + */ + public Object getAdapter( Class adapter ) + { + if ( adapter == IStackFrame.class ) + { + return this; + } + if ( adapter == ICStackFrame.class ) + { + return getCDIStackFrame(); + } + return super.getAdapter( adapter ); + } + + protected void dispose() + { + getCDISession().getEventManager().removeEventListener( this ); + } + + /** + * Retrieves local variables in this stack frame. Returns an empty + * list if there are no local variables. + * + */ + protected List getCDILocalVariables() throws DebugException + { + List list = Collections.EMPTY_LIST; + try + { + ICVariable[] vars = getCDIStackFrame().getLocalVariables(); + list = new ArrayList( vars.length ); + for ( int i = 0; i < vars.length; ++i ) + list.add( new CLocalVariable( this, vars[i] ) ); + } + catch( CDIException e ) + { + targetRequestFailed( MessageFormat.format( "{0} occurred retrieving local variables", new String[] { e.toString() } ), e ); + } + return list; + } + + /** + * Retrieves arguments in this stack frame. Returns an empty list + * if there are no arguments. + * + */ + protected List getCDIArguments() throws DebugException + { + List list = Collections.EMPTY_LIST; + try + { + ICArgument[] args = getCDIStackFrame().getArguments(); + list = new ArrayList( args.length ); + for ( int i = 0; i < args.length; ++i ) + list.add( new CLocalVariable( this, args[i] ) ); + } + catch( CDIException e ) + { + targetRequestFailed( MessageFormat.format( "{0} occurred retrieving arguments", new String[] { e.toString() } ), e ); + } + return list; + } +} diff --git a/debug/org.eclipse.cdt.debug.core/src/org/eclipse/cdt/debug/internal/core/CThread.java b/debug/org.eclipse.cdt.debug.core/src/org/eclipse/cdt/debug/internal/core/CThread.java index 3a8d0b99b8f..fd6416d58cd 100644 --- a/debug/org.eclipse.cdt.debug.core/src/org/eclipse/cdt/debug/internal/core/CThread.java +++ b/debug/org.eclipse.cdt.debug.core/src/org/eclipse/cdt/debug/internal/core/CThread.java @@ -6,10 +6,31 @@ package org.eclipse.cdt.debug.internal.core; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +import org.eclipse.cdt.debug.core.IInstructionStep; import org.eclipse.cdt.debug.core.IState; +import org.eclipse.cdt.debug.core.cdi.CDIException; +import org.eclipse.cdt.debug.core.cdi.ICBreakpoint; +import org.eclipse.cdt.debug.core.cdi.ICEndSteppingRange; +import org.eclipse.cdt.debug.core.cdi.ICSessionObject; +import org.eclipse.cdt.debug.core.cdi.ICSignal; +import org.eclipse.cdt.debug.core.cdi.event.ICChangedEvent; +import org.eclipse.cdt.debug.core.cdi.event.ICDisconnectedEvent; import org.eclipse.cdt.debug.core.cdi.event.ICEvent; import org.eclipse.cdt.debug.core.cdi.event.ICEventListener; +import org.eclipse.cdt.debug.core.cdi.event.ICResumedEvent; +import org.eclipse.cdt.debug.core.cdi.event.ICSteppingEvent; +import org.eclipse.cdt.debug.core.cdi.event.ICSuspendedEvent; +import org.eclipse.cdt.debug.core.cdi.event.ICTerminatedEvent; +import org.eclipse.cdt.debug.core.cdi.model.ICObject; +import org.eclipse.cdt.debug.core.cdi.model.ICStackFrame; import org.eclipse.cdt.debug.core.cdi.model.ICThread; +import org.eclipse.debug.core.DebugEvent; import org.eclipse.debug.core.DebugException; import org.eclipse.debug.core.model.IBreakpoint; import org.eclipse.debug.core.model.IStackFrame; @@ -23,14 +44,47 @@ import org.eclipse.debug.core.model.IThread; */ public class CThread extends CDebugElement implements IThread, - IState, + IState, + IInstructionStep, ICEventListener { /** - * Underlying CDI target. + * Underlying CDI thread. */ private ICThread fCDIThread; + /** + * Collection of stack frames + */ + private List fStackFrames; + + /** + * Whether running. + */ + private boolean fRunning; + + /** + * Whether children need to be refreshed. Set to + * true when stack frames are re-used + * on the next suspend. + */ + private boolean fRefreshChildren = true; + + /** + * The current state identifier. + */ + private int fCurrentStateId = IState.UNKNOWN; + + /** + * The current state info. + */ + private Object fCurrentStateInfo = null; + + /** + * The debug configuration of this session. + */ + private CDebugConfiguration fConfig; + /** * Constructor for CThread. * @param target @@ -39,6 +93,22 @@ public class CThread extends CDebugElement { super( target ); setCDIThread( cdiThread ); + fConfig = new CDebugConfiguration( getCDISession() ); + initialize(); + getCDISession().getEventManager().addEventListener( this ); + } + + /** + * Thread initialization: + */ + protected void initialize() + { + fStackFrames = Collections.EMPTY_LIST; + setRunning( !getCDIThread().isSuspended() ); } /* (non-Javadoc) @@ -46,7 +116,8 @@ public class CThread extends CDebugElement */ public IStackFrame[] getStackFrames() throws DebugException { - return null; + List list = computeStackFrames(); + return (IStackFrame[])list.toArray( new IStackFrame[list.size()] ); } /* (non-Javadoc) @@ -54,9 +125,217 @@ public class CThread extends CDebugElement */ public boolean hasStackFrames() throws DebugException { + try + { + return computeStackFrames().size() > 0; + } + catch( DebugException e ) + { + // do not throw an exception if the thread resumed while determining + // whether stack frames are present + } return false; } + /** + * @see computeStackFrames() + * + * @param refreshChildren whether or not this method should request new stack + * frames from the target + */ + protected synchronized List computeStackFrames( boolean refreshChildren ) throws DebugException + { + if ( isSuspended() ) + { + if ( isTerminated() ) + { + fStackFrames = Collections.EMPTY_LIST; + } + else if ( refreshChildren ) + { + if ( fStackFrames.isEmpty() ) + { + fStackFrames = createAllStackFrames(); + if ( fStackFrames.isEmpty() ) + { + //leave fRefreshChildren == true + //bug 6393 + // ????? + return fStackFrames; + } + } + ICStackFrame[] frames = getCDIStackFrames(); + // compute new or removed stack frames + int offset = 0, length = frames.length; + if ( length > fStackFrames.size() ) + { + // compute new frames + offset = length - fStackFrames.size(); + for ( int i = offset - 1; i >= 0; i-- ) + { + CStackFrame newStackFrame = new CStackFrame( this, frames[i] ); + fStackFrames.add( 0, newStackFrame ); + } + length = fStackFrames.size() - offset; + } + else if ( length < fStackFrames.size() ) + { + // compute removed children + int removed = fStackFrames.size() - length; + for ( int i = 0; i < removed; i++ ) + { + fStackFrames.remove( 0 ); + } + } + else + { + if ( frames.length == 0 ) + { + fStackFrames = Collections.EMPTY_LIST; + } + else + { + // same number of frames - if top frames are in different + // method, replace all frames + ICStackFrame newTop = frames[0]; + ICStackFrame oldTop = ((CStackFrame)fStackFrames.get( 0 ) ).getLastCDIStackFrame(); + if (!CStackFrame.equalFrame( newTop, oldTop ) ) + { + fStackFrames = createAllStackFrames(); + offset = fStackFrames.size(); + } + } + } + // update preserved frames + if ( offset < fStackFrames.size() ) + { + updateStackFrames( frames, offset, fStackFrames, length ); + } + } + fRefreshChildren = false; + } + else + { + return Collections.EMPTY_LIST; + } + return fStackFrames; + } + + /** + * Retrieves and returns all underlying stack frames + * + * @return list of StackFrame + * @exception DebugException if this method fails. Reasons include: + * + */ + protected ICStackFrame[] getCDIStackFrames() throws DebugException + { + try + { + return getCDIThread().getStackFrames(); + } + catch( CDIException e ) + { + requestFailed( MessageFormat.format( "{0} occurred retrieving stack frames.", new String[] { e.toString() } ), e ); + // execution will not reach this line, as + // #targetRequestFailed will thrown an exception + return null; + } + } + + /** + * Replaces the underlying stack frame objects in the preserved frames + * list with the current underlying stack frames. + * + * @param newFrames list of current underlying ICStackFrames. + * Frames from this list are assigned to the underlying frames in + * the oldFrames list. + * @param offset the offset in the lists at which to start replacing + * the old underlying frames + * @param oldFrames list of preserved frames, of type CStackFrame + * @param length the number of frames to replace + */ + protected void updateStackFrames( ICStackFrame[] newFrames, + int offset, + List oldFrames, + int length ) throws DebugException + { + for ( int i = 0; i < length; i++ ) + { + CStackFrame frame = (CStackFrame)oldFrames.get( offset ); + frame.setCDIStackFrame( newFrames[offset] ); + offset++; + } + } + + /** + * Returns this thread's current stack frames as a list, computing + * them if required. Returns an empty collection if this thread is + * not currently suspended, or this thread is terminated. This + * method should be used internally to get the current stack frames, + * instead of calling #getStackFrames(), which makes a + * copy of the current list. + *

+ * Before a thread is resumed a call must be made to one of:

+ * If stack frames are disposed before a thread is resumed, stack frames + * are completely re-computed on the next call to this method. If stack + * frames are to be preserved, this method will attempt to re-use any stack + * frame objects which represent the same stack frame as on the previous + * suspend. Stack frames are cached until a subsequent call to preserve + * or dispose stack frames. + *

+ * + * @return list of IStackFrame + * @exception DebugException if this method fails. Reasons include: + * + */ + public List computeStackFrames() throws DebugException + { + return computeStackFrames( fRefreshChildren ); + } + + /** + * @see CThread#computeStackFrames() + * + * This method differs from computeStackFrames() in that it + * always requests new stack frames from the target. As this is + * an expensive operation, this method should only be used + * by clients who know for certain that the stack frames + * on the target have changed. + */ + public List computeNewStackFrames() throws DebugException + { + return computeStackFrames( true ); + } + + /** + * Helper method for #computeStackFrames() to create all + * underlying stack frames. + * + * @exception DebugException if this method fails. Reasons include: + * + */ + protected List createAllStackFrames() throws DebugException + { + ICStackFrame[] frames = getCDIStackFrames(); + List list= new ArrayList( frames.length ); + for ( int i = 0; i < frames.length; ++i ) + { + CStackFrame newStackFrame = new CStackFrame( this, frames[i] ); + list.add( newStackFrame ); + } + return list; + } + /* (non-Javadoc) * @see org.eclipse.debug.core.model.IThread#getPriority() */ @@ -70,7 +349,8 @@ public class CThread extends CDebugElement */ public IStackFrame getTopStackFrame() throws DebugException { - return null; + List c = computeStackFrames(); + return ( c.isEmpty() ) ? null : (IStackFrame)c.get( 0 ); } /* (non-Javadoc) @@ -78,7 +358,7 @@ public class CThread extends CDebugElement */ public String getName() throws DebugException { - return null; + return "Thread " + getCDIThread().getId(); } /* (non-Javadoc) @@ -94,6 +374,52 @@ public class CThread extends CDebugElement */ public void handleDebugEvent( ICEvent event ) { + ICObject source = event.getSource(); + if ( source.getCDITarget().equals( getCDITarget() ) ) + { + if ( event instanceof ICSuspendedEvent ) + { + if ( source instanceof ICThread ) + { + handleSuspendedEvent( (ICSuspendedEvent)event ); + } + } + else if ( event instanceof ICResumedEvent ) + { + if ( source instanceof ICThread ) + { + handleResumedEvent( (ICResumedEvent)event ); + } + } + else if ( event instanceof ICTerminatedEvent ) + { + if ( source instanceof ICThread ) + { + handleTerminatedEvent( (ICTerminatedEvent)event ); + } + } + else if ( event instanceof ICDisconnectedEvent ) + { + if ( source instanceof ICThread ) + { + handleDisconnectedEvent( (ICDisconnectedEvent)event ); + } + } + else if ( event instanceof ICChangedEvent ) + { + if ( source instanceof ICThread ) + { + handleChangedEvent( (ICChangedEvent)event ); + } + } + else if ( event instanceof ICSteppingEvent ) + { + if ( source instanceof ICThread ) + { + handleSteppingEvent( (ICSteppingEvent)event ); + } + } + } } /* (non-Javadoc) @@ -101,7 +427,7 @@ public class CThread extends CDebugElement */ public boolean canResume() { - return false; + return isSuspended() && !getDebugTarget().isSuspended(); } /* (non-Javadoc) @@ -109,7 +435,7 @@ public class CThread extends CDebugElement */ public boolean canSuspend() { - return false; + return !isSuspended(); } /* (non-Javadoc) @@ -117,7 +443,7 @@ public class CThread extends CDebugElement */ public boolean isSuspended() { - return false; + return !fRunning && !isTerminated(); } /* (non-Javadoc) @@ -125,6 +451,23 @@ public class CThread extends CDebugElement */ public void resume() throws DebugException { + if ( !isSuspended() ) + { + return; + } + try + { + setRunning( true ); + disposeStackFrames(); + fireResumeEvent( DebugEvent.CLIENT_REQUEST ); + getCDIThread().resume(); + } + catch( CDIException e ) + { + setRunning( false ); + fireSuspendEvent( DebugEvent.CLIENT_REQUEST ); + targetRequestFailed( MessageFormat.format( "{0} occurred resuming thread.", new String[] { e.toString()} ), e ); + } } /* (non-Javadoc) @@ -132,6 +475,22 @@ public class CThread extends CDebugElement */ public void suspend() throws DebugException { + if ( isSuspended() ) + { + return; + } + try + { + setRunning( false ); + getCDIThread().suspend(); + fireSuspendEvent( DebugEvent.CLIENT_REQUEST ); + } + catch( CDIException e ) + { + setRunning( true ); + fireResumeEvent( DebugEvent.CLIENT_REQUEST ); + targetRequestFailed( MessageFormat.format( "{0} occurred suspending thread.", new String[] { e.toString()} ), e ); + } } /* (non-Javadoc) @@ -139,7 +498,7 @@ public class CThread extends CDebugElement */ public boolean canStepInto() { - return false; + return canStep(); } /* (non-Javadoc) @@ -147,7 +506,7 @@ public class CThread extends CDebugElement */ public boolean canStepOver() { - return false; + return canStep(); } /* (non-Javadoc) @@ -155,7 +514,26 @@ public class CThread extends CDebugElement */ public boolean canStepReturn() { - return false; + return canStep(); + } + + /** + * Returns whether this thread is in a valid state to + * step. + * + * @return whether this thread is in a valid state to + * step + */ + protected boolean canStep() + { + try + { + return fConfig.supportsStepping() && isSuspended() && getTopStackFrame() != null; + } + catch( DebugException e ) + { + return false; + } } /* (non-Javadoc) @@ -163,7 +541,7 @@ public class CThread extends CDebugElement */ public boolean isStepping() { - return false; + return getCurrentStateId() == IState.STEPPING; // ???? } /* (non-Javadoc) @@ -171,6 +549,23 @@ public class CThread extends CDebugElement */ public void stepInto() throws DebugException { + if ( !canStepInto() ) + { + return; + } + try + { + setRunning( true ); + preserveStackFrames(); + fireResumeEvent( DebugEvent.STEP_INTO ); + getCDIThread().stepInto(); + } + catch( CDIException e ) + { + setRunning( false ); + fireSuspendEvent( DebugEvent.STEP_INTO ); + targetRequestFailed( MessageFormat.format( "{0} occurred stepping in thread.", new String[] { e.toString()} ), e ); + } } /* (non-Javadoc) @@ -178,6 +573,23 @@ public class CThread extends CDebugElement */ public void stepOver() throws DebugException { + if ( !canStepOver() ) + { + return; + } + try + { + setRunning( true ); + preserveStackFrames(); + fireResumeEvent( DebugEvent.STEP_OVER ); + getCDIThread().stepInto(); + } + catch( CDIException e ) + { + setRunning( false ); + fireSuspendEvent( DebugEvent.STEP_OVER ); + targetRequestFailed( MessageFormat.format( "{0} occurred stepping in thread.", new String[] { e.toString()} ), e ); + } } /* (non-Javadoc) @@ -185,6 +597,23 @@ public class CThread extends CDebugElement */ public void stepReturn() throws DebugException { + if ( !canStepReturn() ) + { + return; + } + try + { + setRunning( true ); + preserveStackFrames(); + fireResumeEvent( DebugEvent.STEP_RETURN ); + getCDIThread().stepInto(); + } + catch( CDIException e ) + { + setRunning( false ); + fireSuspendEvent( DebugEvent.STEP_RETURN ); + targetRequestFailed( MessageFormat.format( "{0} occurred stepping in thread.", new String[] { e.toString()} ), e ); + } } /* (non-Javadoc) @@ -192,7 +621,7 @@ public class CThread extends CDebugElement */ public boolean canTerminate() { - return false; + return !isTerminated(); } /* (non-Javadoc) @@ -200,7 +629,7 @@ public class CThread extends CDebugElement */ public boolean isTerminated() { - return false; + return getDebugTarget().isTerminated(); } /* (non-Javadoc) @@ -208,6 +637,7 @@ public class CThread extends CDebugElement */ public void terminate() throws DebugException { + getDebugTarget().terminate(); } /** @@ -239,15 +669,26 @@ public class CThread extends CDebugElement */ protected void setRunning( boolean running ) { + fRunning = running; } /** - * Sets whether this thread is terminated + * Preserves stack frames to be used on the next suspend event. + * Iterates through all current stack frames, setting their + * state as invalid. This method should be called before this thread + * is resumed, when stack frames are to be re-used when it later + * suspends. * - * @param terminated whether this thread is terminated + * @see computeStackFrames() */ - protected void setTerminated( boolean terminated ) + protected void preserveStackFrames() { + fRefreshChildren = true; + Iterator frames = fStackFrames.iterator(); + while( frames.hasNext() ) + { + ((CStackFrame)frames.next()).setCDIStackFrame( null ); + } } /** @@ -259,6 +700,13 @@ public class CThread extends CDebugElement */ protected synchronized void disposeStackFrames() { + Iterator it = fStackFrames.iterator(); + while( it.hasNext() ) + { + ((CStackFrame)it.next()).dispose(); + } + fStackFrames = Collections.EMPTY_LIST; + fRefreshChildren = true; } /** @@ -267,17 +715,185 @@ public class CThread extends CDebugElement */ protected void terminated() { - setTerminated( true ); - setRunning( false ); + setRunning( false ); + cleanup(); fireTerminateEvent(); } /* (non-Javadoc) - * @see org.eclipse.cdt.debug.core.IState#getCurrentState() + * @see org.eclipse.cdt.debug.core.IState#getCurrentStateId() */ - public int getCurrentState() + public int getCurrentStateId() { - return IState.UNKNOWN; + return fCurrentStateId; } + /** + * Sets the current state identifier. + * + * @param id the identifier + */ + private void setCurrentStateId( int id ) + { + fCurrentStateId = id; + } + + /* (non-Javadoc) + * @see org.eclipse.cdt.debug.core.IState#getCurrentStateInfo() + */ + public Object getCurrentStateInfo() + { + return fCurrentStateInfo; + } + + /** + * Sets the info object of the current state. + * + * @param id the info object + */ + private void setCurrentStateInfo( Object info ) + { + fCurrentStateInfo = info; + } + + /* (non-Javadoc) + * @see org.eclipse.cdt.debug.core.IInstructionStep#canStepIntoInstruction() + */ + public boolean canStepIntoInstruction() + { + return canStepInto(); + } + + /* (non-Javadoc) + * @see org.eclipse.cdt.debug.core.IInstructionStep#canStepOverInstruction() + */ + public boolean canStepOverInstruction() + { + return canStepOver(); + } + + /* (non-Javadoc) + * @see org.eclipse.cdt.debug.core.IInstructionStep#stepIntoInstruction() + */ + public void stepIntoInstruction() throws DebugException + { + if ( !canStepIntoInstruction() ) + { + return; + } + try + { + setRunning( true ); + preserveStackFrames(); + fireResumeEvent( DebugEvent.STEP_INTO ); + getCDIThread().stepIntoInstruction(); + } + catch( CDIException e ) + { + setRunning( false ); + fireSuspendEvent( DebugEvent.STEP_INTO ); + targetRequestFailed( MessageFormat.format( "{0} occurred stepping in thread.", new String[] { e.toString()} ), e ); + } + } + + /* (non-Javadoc) + * @see org.eclipse.cdt.debug.core.IInstructionStep#stepOverInstruction() + */ + public void stepOverInstruction() throws DebugException + { + if ( !canStepOverInstruction() ) + { + return; + } + try + { + setRunning( true ); + preserveStackFrames(); + fireResumeEvent( DebugEvent.STEP_OVER ); + getCDIThread().stepOverInstruction(); + } + catch( CDIException e ) + { + setRunning( false ); + fireSuspendEvent( DebugEvent.STEP_OVER ); + targetRequestFailed( MessageFormat.format( "{0} occurred stepping in thread.", new String[] { e.toString()} ), e ); + } + } + + private void handleSuspendedEvent( ICSuspendedEvent event ) + { + setRunning( false ); + setCurrentStateId( IState.SUSPENDED ); + ICSessionObject reason = event.getReason(); + setCurrentStateInfo( reason ); + if ( reason instanceof ICEndSteppingRange ) + { + handleEndSteppingRange( (ICEndSteppingRange)reason ); + } + else if ( reason instanceof ICBreakpoint ) + { + handleBreakpointHit( (ICBreakpoint)reason ); + } + else if ( reason instanceof ICSignal ) + { + handleSuspendedBySignal( (ICSignal)reason ); + } + } + + private void handleResumedEvent( ICResumedEvent event ) + { + setRunning( true ); + setCurrentStateId( IState.RUNNING ); + setCurrentStateInfo( null ); + fireResumeEvent( DebugEvent.UNSPECIFIED ); + } + + private void handleEndSteppingRange( ICEndSteppingRange endSteppingRange ) + { + fireSuspendEvent( DebugEvent.UNSPECIFIED ); + } + + private void handleBreakpointHit( ICBreakpoint breakpoint ) + { + fireSuspendEvent( DebugEvent.BREAKPOINT ); + } + + private void handleSuspendedBySignal( ICSignal signal ) + { + fireSuspendEvent( DebugEvent.UNSPECIFIED ); + } + + private void handleTerminatedEvent( ICTerminatedEvent event ) + { + setCurrentStateId( IState.TERMINATED ); + setCurrentStateInfo( null ); + terminated(); + } + + private void handleDisconnectedEvent( ICDisconnectedEvent event ) + { + setCurrentStateId( IState.TERMINATED ); + setCurrentStateInfo( null ); + terminated(); + } + + private void handleChangedEvent( ICChangedEvent event ) + { + } + + private void handleSteppingEvent( ICSteppingEvent event ) + { + setCurrentStateId( IState.STEPPING ); + setCurrentStateInfo( null ); + } + + /** + * Cleans up the internal state of this thread. + * + */ + protected void cleanup() + { + getCDISession().getEventManager().removeEventListener( this ); + disposeStackFrames(); + } }