/*******************************************************************************
* Copyright (c) 2000, 2010 QNX Software Systems and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* QNX Software Systems - Initial API and implementation
* Stefan Bylund (Enea, steby@enea.se) - patch for bug 155464
* Ken Ryall (Nokia) - Support for breakpoint actions (bug 118308)
* Ling Wang (Nokia) - Bug 176077
* Denis Pilat (ST) - Bug 205017
*******************************************************************************/
package org.eclipse.cdt.debug.internal.core.model;
import com.ibm.icu.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import org.eclipse.cdt.debug.core.CDebugCorePlugin;
import org.eclipse.cdt.debug.core.cdi.CDIException;
import org.eclipse.cdt.debug.core.cdi.ICDIBreakpointHit;
import org.eclipse.cdt.debug.core.cdi.ICDIEndSteppingRange;
import org.eclipse.cdt.debug.core.cdi.ICDILocation;
import org.eclipse.cdt.debug.core.cdi.ICDISessionObject;
import org.eclipse.cdt.debug.core.cdi.ICDISignalReceived;
import org.eclipse.cdt.debug.core.cdi.ICDIWatchpointTrigger;
import org.eclipse.cdt.debug.core.cdi.event.ICDIChangedEvent;
import org.eclipse.cdt.debug.core.cdi.event.ICDIDestroyedEvent;
import org.eclipse.cdt.debug.core.cdi.event.ICDIDisconnectedEvent;
import org.eclipse.cdt.debug.core.cdi.event.ICDIEvent;
import org.eclipse.cdt.debug.core.cdi.event.ICDIEventListener;
import org.eclipse.cdt.debug.core.cdi.event.ICDIResumedEvent;
import org.eclipse.cdt.debug.core.cdi.event.ICDISuspendedEvent;
import org.eclipse.cdt.debug.core.cdi.model.ICDIDisposable;
import org.eclipse.cdt.debug.core.cdi.model.ICDIObject;
import org.eclipse.cdt.debug.core.cdi.model.ICDIStackFrame;
import org.eclipse.cdt.debug.core.cdi.model.ICDITargetConfiguration;
import org.eclipse.cdt.debug.core.cdi.model.ICDITargetConfiguration2;
import org.eclipse.cdt.debug.core.cdi.model.ICDIThread;
import org.eclipse.cdt.debug.core.model.CDebugElementState;
import org.eclipse.cdt.debug.core.model.ICDebugElementStatus;
import org.eclipse.cdt.debug.core.model.ICStackFrame;
import org.eclipse.cdt.debug.core.model.ICThread;
import org.eclipse.cdt.debug.core.model.IDummyStackFrame;
import org.eclipse.cdt.debug.core.model.IMoveToAddress;
import org.eclipse.cdt.debug.core.model.IMoveToLine;
import org.eclipse.cdt.debug.core.model.IRestart;
import org.eclipse.cdt.debug.core.model.IResumeAtAddress;
import org.eclipse.cdt.debug.core.model.IResumeAtLine;
import org.eclipse.cdt.debug.core.model.IResumeWithoutSignal;
import org.eclipse.cdt.debug.core.model.IRunToAddress;
import org.eclipse.cdt.debug.core.model.IRunToLine;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.IAdaptable;
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.IMemoryBlockRetrieval;
import org.eclipse.debug.core.model.IStackFrame;
/**
* A thread in a C/C++ debug model.
*/
public class CThread extends CDebugElement implements ICThread, IRestart, IResumeWithoutSignal, ICDIEventListener {
private final static int MAX_STACK_DEPTH = 100;
/**
* Underlying CDI thread.
*/
private ICDIThread fCDIThread;
/**
* Collection of stack frames
*/
private ArrayList fStackFrames;
/**
* Whether children need to be refreshed. Set to <code>true</code> when stack frames are re-used on the next suspend.
*/
private boolean fRefreshChildren = true;
/**
* The debug configuration of this session.
*/
private ICDITargetConfiguration fConfig;
/**
* Whether this thread is current.
*/
private boolean fIsCurrent = false;
/**
* The depth of the current stack.
*/
private int fLastStackDepth = 0;
/**
* Whether this thread is disposed.
*/
private boolean fDisposed = false;
/**
* Constructor for CThread.
*/
public CThread( CDebugTarget target, ICDIThread cdiThread ) {
super( target );
setState( cdiThread.isSuspended() ? CDebugElementState.SUSPENDED : CDebugElementState.RESUMED );
setCDIThread( cdiThread );
fConfig = getCDITarget().getConfiguration();
initialize();
getCDISession().getEventManager().addEventListener( this );
}
protected void initialize() {
fStackFrames = new ArrayList();
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.model.IThread#getStackFrames()
*/
public IStackFrame[] getStackFrames() throws DebugException {
List list = Collections.EMPTY_LIST;
try {
list = computeStackFrames();
}
catch( DebugException e ) {
setStatus( ICDebugElementStatus.ERROR, e.getStatus().getMessage() );
throw e;
}
return (IStackFrame[])list.toArray( new IStackFrame[list.size()] );
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.model.IThread#hasStackFrames()
*/
public boolean hasStackFrames() throws DebugException {
// Always return true to postpone the stack frames request.
// But not if the thread is already resumed. This fixes flickering in the Debug View.
if (getState().equals( CDebugElementState.RESUMED ))
return false;
return true;
}
/**
* @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 = new ArrayList();
}
else if ( refreshChildren ) {
// Remove dummy frame
if ( fStackFrames.size() > 0 ) {
Object frame = fStackFrames.get( fStackFrames.size() - 1 );
if ( frame instanceof IDummyStackFrame ) {
fStackFrames.remove( frame );
}
}
// Retrieve stack frames from the backend
int depth = getStackDepth();
if ( depth >= getMaxStackDepth() )
depth = getMaxStackDepth() - 1;
ICDIStackFrame[] frames = ( depth != 0 ) ? getCDIStackFrames( 0, depth - 1 ) : new ICDIStackFrame[0];
// Safety precaution in case getting the stack frames failed to get us as many as it said
depth = frames.length;
if ( fStackFrames.isEmpty() ) {
if ( frames.length > 0 ) {
addStackFrames( frames, 0, frames.length, false );
}
}
else {
int diff = depth - getLastStackDepth();
int offset = ( diff > 0 ) ? frames.length - diff : 0;
int length = ( diff > 0 ) ? diff : -diff;
if (offset < 0 || !compareStackFrames( frames, fStackFrames, offset, length ) ) {
// replace all frames
disposeStackFrames( 0, fStackFrames.size() );
addStackFrames( frames, 0, frames.length, false );
}
if ( diff < 0 ) {
// stepping out of the last frame
disposeStackFrames( 0, getLastStackDepth() - depth );
if ( frames.length > 0 ) {
updateStackFrames( frames, 0, fStackFrames, fStackFrames.size() );
if ( fStackFrames.size() < frames.length ) {
addStackFrames( frames, fStackFrames.size(), frames.length - fStackFrames.size(), true );
}
}
}
else if ( diff > 0 ) {
// stepping into a new frame
disposeStackFrames( frames.length - depth + getLastStackDepth(), depth - getLastStackDepth() );
addStackFrames( frames, 0, depth - getLastStackDepth(), false );
updateStackFrames( frames, depth - getLastStackDepth(), fStackFrames, frames.length - depth + getLastStackDepth() );
}
else { // diff == 0
if ( depth != 0 ) {
// we are in the same frame
updateStackFrames( frames, 0, fStackFrames, frames.length );
}
}
}
if ( depth > getMaxStackDepth() ) {
fStackFrames.add( new CDummyStackFrame( this ) );
}
setLastStackDepth( depth );
setRefreshChildren( false );
}
}
return fStackFrames;
}
/**
* Compares the lists of the old and new frames.
*
* @param newFrames the array of the new frames
* @param oldFrames the list of the old frames
* @param offset the offset in the new frames array
* @param length the number of frames to compare
*
* @return <code>true</code> if all frames are same
*/
private boolean compareStackFrames( ICDIStackFrame[] newFrames, List oldFrames, int offset, int length ) {
if (offset<0) return false;
int index = offset;
Iterator it = oldFrames.iterator();
while( it.hasNext() && index < newFrames.length ) {
CStackFrame frame = (CStackFrame)it.next();
if ( !frame.getCDIStackFrame().equals( newFrames[index++] ) )
return false;
}
return true;
}
/**
* Retrieves and returns all underlying stack frames
*
* @return list of <code>StackFrame</code>
* @exception DebugException if this method fails. Reasons include:
* <ul>
* </ul>
*/
protected ICDIStackFrame[] getCDIStackFrames() throws DebugException {
return new ICDIStackFrame[0];
}
/**
* Retrieves and returns underlying stack frames between <code>lowFrame<code/>
* and <code>highFrame<code/>.
*
* @return list of <code>StackFrame</code>
* @exception DebugException if this method fails. Reasons include:
* <ul>
* </ul>
*/
protected ICDIStackFrame[] getCDIStackFrames( int lowFrame, int highFrame ) throws DebugException {
try {
final ICDIThread cdiThread = getCDIThread();
if (cdiThread != null) {
return cdiThread.getStackFrames( lowFrame, highFrame );
}
}
catch( CDIException e ) {
setStatus( ICDebugElementStatus.WARNING, MessageFormat.format( CoreModelMessages.getString( "CThread.0" ), new String[]{ e.getMessage() } ) ); //$NON-NLS-1$
targetRequestFailed( e.getMessage(), null );
}
return new ICDIStackFrame[0];
}
/**
* Replaces the underlying stack frame objects in the preserved frames list with the current underlying stack frames.
*
* @param newFrames list of current underlying <code>ICDIStackFrame</code>s. Frames from this list are assigned to the underlying frames in the
* <code>oldFrames</code> 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 <code>CStackFrame</code>
* @param length the number of frames to replace
*/
protected void updateStackFrames( ICDIStackFrame[] 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++;
}
}
protected void addStackFrames( ICDIStackFrame[] newFrames, int startIndex, int length, boolean append ) {
if ( newFrames.length >= startIndex + length ) {
for( int i = 0; i < length; ++i ) {
if ( append )
fStackFrames.add( new CStackFrame( this, newFrames[startIndex + i] ) );
else
fStackFrames.add( i, new CStackFrame( this, newFrames[startIndex + i] ) );
}
}
}
/**
* 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 <code>#getStackFrames()</code>,
* which makes a copy of the current list.
* <p>
* Before a thread is resumed a call must be made to one of:
* <ul>
* <li><code>preserveStackFrames()</code></li>
* <li><code>disposeStackFrames()</code></li>
* </ul>
* 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.
* </p>
*
* @return list of <code>IStackFrame</code>
* @exception DebugException if this method fails. Reasons include:
* <ul>
* </ul>
*/
public synchronized List computeStackFrames() throws DebugException {
return computeStackFrames( refreshChildren() );
}
/**
* @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 <code>#computeStackFrames()</code> to create all underlying stack frames.
*
* @exception DebugException if this method fails. Reasons include:
* <ul>
* </ul>
*/
protected List createAllStackFrames( int depth, ICDIStackFrame[] frames ) throws DebugException {
List list = new ArrayList( frames.length );
for( int i = 0; i < frames.length; ++i ) {
list.add( new CStackFrame( this, frames[i] ) );
}
if ( depth > frames.length ) {
list.add( new CDummyStackFrame( this ) );
}
return list;
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.model.IThread#getPriority()
*/
public int getPriority() throws DebugException {
return 0;
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.model.IThread#getTopStackFrame()
*/
public IStackFrame getTopStackFrame() throws DebugException {
List c = computeStackFrames();
return (c.isEmpty()) ? null : (IStackFrame)c.get( 0 );
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.model.IThread#getName()
*/
public String getName() throws DebugException {
final ICDIThread cdiThread = getCDIThread();
return cdiThread != null ? cdiThread.toString() : ""; //$NON-NLS-1$
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.model.IThread#getBreakpoints()
*/
public IBreakpoint[] getBreakpoints() {
List list = new ArrayList( 1 );
if ( isSuspended() ) {
IBreakpoint bkpt = null;
if ( getCurrentStateInfo() instanceof ICDIBreakpointHit )
bkpt = ((CDebugTarget)getDebugTarget()).getBreakpointManager().getBreakpoint( ((ICDIBreakpointHit)getCurrentStateInfo()).getBreakpoint() );
else if ( getCurrentStateInfo() instanceof ICDIWatchpointTrigger )
bkpt = ((CDebugTarget)getDebugTarget()).getBreakpointManager().getBreakpoint( ((ICDIWatchpointTrigger)getCurrentStateInfo()).getWatchpoint() );
if ( bkpt != null )
list.add( bkpt );
}
return (IBreakpoint[])list.toArray( new IBreakpoint[list.size()] );
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.core.cdi.event.ICDIEventListener#handleDebugEvents(org.eclipse.cdt.debug.core.cdi.event.ICDIEvent[])
*/
public void handleDebugEvents( ICDIEvent[] events ) {
if ( isDisposed() )
return;
for( int i = 0; i < events.length; i++ ) {
ICDIEvent event = events[i];
ICDIObject source = event.getSource();
final ICDIThread cdiThread = getCDIThread();
if ( source instanceof ICDIThread && cdiThread != null && source.equals( cdiThread ) ) {
if ( event instanceof ICDISuspendedEvent ) {
handleSuspendedEvent( (ICDISuspendedEvent)event );
}
else if ( event instanceof ICDIResumedEvent ) {
handleResumedEvent( (ICDIResumedEvent)event );
}
else if ( event instanceof ICDIDestroyedEvent ) {
handleTerminatedEvent( (ICDIDestroyedEvent)event );
}
else if ( event instanceof ICDIDisconnectedEvent ) {
handleDisconnectedEvent( (ICDIDisconnectedEvent)event );
}
else if ( event instanceof ICDIChangedEvent ) {
handleChangedEvent( (ICDIChangedEvent)event );
}
}
}
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.model.ISuspendResume#canResume()
*/
public boolean canResume() {
return ( fConfig.supportsResume() && isSuspended() );
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.core.model.IRunToLine#canRunToLine(org.eclipse.core.resources.IFile, int)
*/
public boolean canRunToLine( IFile file, int lineNumber ) {
return canRunToLine( file.getLocation().lastSegment(), lineNumber );
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.core.model.IRunToLine#canRunToLine(java.lang.String, int)
*/
public boolean canRunToLine( String fileName, int lineNumber ) {
return canResume();
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.model.ISuspendResume#canSuspend()
*/
public boolean canSuspend() {
CDebugElementState state = getState();
return ( fConfig.supportsSuspend() && (state.equals( CDebugElementState.RESUMED ) || state.equals( CDebugElementState.STEPPED )) );
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.model.ISuspendResume#isSuspended()
*/
public boolean isSuspended() {
return getState().equals( CDebugElementState.SUSPENDED );
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.model.ISuspendResume#resume()
*/
public void resume() throws DebugException {
if ( !canResume() )
return;
CDebugElementState oldState = getState();
setState( CDebugElementState.RESUMING );
try {
final ICDIThread cdiThread = getCDIThread();
if (cdiThread != null) {
cdiThread.resume( false );
}
}
catch( CDIException e ) {
setState( oldState );
targetRequestFailed( e.getMessage(), null );
}
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.core.model.IRunToLine#runToLine(org.eclipse.core.resources.IFile, int, boolean)
*/
public void runToLine( IFile file, int lineNumber, boolean skipBreakpoints ) throws DebugException {
runToLine( file.getLocation().lastSegment(), lineNumber, skipBreakpoints );
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.core.model.IRunToLine#runToLine(java.lang.String, int, boolean)
*/
public void runToLine( String fileName, int lineNumber, boolean skipBreakpoints ) throws DebugException {
if ( !canRunToLine( fileName, lineNumber ) )
return;
if ( skipBreakpoints ) {
((CDebugTarget)getDebugTarget()).skipBreakpoints( true );
}
CDebugElementState oldState = getState();
setState( CDebugElementState.RESUMING );
ICDILocation location = getCDITarget().createLineLocation( fileName, lineNumber );
try {
final ICDIThread cdiThread = getCDIThread();
if (cdiThread != null) {
cdiThread.stepUntil( location );
}
}
catch( CDIException e ) {
setState( oldState );
if ( skipBreakpoints ) {
((CDebugTarget)getDebugTarget()).skipBreakpoints( false );
}
targetRequestFailed( e.getMessage(), e );
}
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.model.ISuspendResume#suspend()
*/
public void suspend() throws DebugException {
if ( !canSuspend() )
return;
CDebugElementState oldState = getState();
setState( CDebugElementState.SUSPENDING );
try {
final ICDIThread cdiThread = getCDIThread();
if (cdiThread != null) {
cdiThread.suspend();
}
}
catch( CDIException e ) {
setState( oldState );
targetRequestFailed( e.getMessage(), null );
}
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.model.IStep#canStepInto()
*/
public boolean canStepInto() {
return canStep();
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.model.IStep#canStepOver()
*/
public boolean canStepOver() {
return canStep();
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.model.IStep#canStepReturn()
*/
public boolean canStepReturn() {
if ( !fConfig.supportsStepping() || !canResume() ) {
return false;
}
return ( fStackFrames.size() > 1 );
}
/**
* 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() {
if ( !fConfig.supportsStepping() || !isSuspended() ) {
return false;
}
return !fStackFrames.isEmpty();
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.model.IStep#isStepping()
*/
public boolean isStepping() {
return ( getState().equals( CDebugElementState.STEPPING ) ) || ( getState().equals( CDebugElementState.STEPPED ) );
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.model.IStep#stepInto()
*/
public void stepInto() throws DebugException {
if ( !canStepInto() )
return;
CDebugElementState oldState = getState();
setState( CDebugElementState.STEPPING );
try {
final ICDIThread cdiThread = getCDIThread();
if (cdiThread != null) {
if ( !isInstructionsteppingEnabled() ) {
cdiThread.stepInto( 1 );
}
else {
cdiThread.stepIntoInstruction( 1 );
}
}
}
catch( CDIException e ) {
setState( oldState );
targetRequestFailed( e.getMessage(), null );
}
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.model.IStep#stepOver()
*/
public void stepOver() throws DebugException {
if ( !canStepOver() )
return;
CDebugElementState oldState = getState();
setState( CDebugElementState.STEPPING );
try {
final ICDIThread cdiThread = getCDIThread();
if (cdiThread != null) {
if ( !isInstructionsteppingEnabled() ) {
cdiThread.stepOver( 1 );
}
else {
cdiThread.stepOverInstruction( 1 );
}
}
}
catch( CDIException e ) {
setState( oldState );
targetRequestFailed( e.getMessage(), null );
}
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.model.IStep#stepReturn()
*/
public void stepReturn() throws DebugException {
if ( !canStepReturn() )
return;
IStackFrame[] frames = getStackFrames();
if ( frames.length == 0 )
return;
CStackFrame f = (CStackFrame)frames[0];
CDebugElementState oldState = getState();
setState( CDebugElementState.STEPPING );
try {
f.doStepReturn();
}
catch( DebugException e ) {
setState( oldState );
throw e;
}
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.model.ITerminate#canTerminate()
*/
public boolean canTerminate() {
return getDebugTarget().canTerminate();
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.model.ITerminate#isTerminated()
*/
public boolean isTerminated() {
return getDebugTarget().isTerminated();
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.model.ITerminate#terminate()
*/
public void terminate() throws DebugException {
getDebugTarget().terminate();
}
/**
* Sets the underlying CDI thread that this model object is a proxy to.
*
* @param thread the underlying CDI thread
*/
protected void setCDIThread( ICDIThread cdiThread ) {
fCDIThread = cdiThread;
}
/**
* Returns the underlying CDI thread that this model object is a proxy to.
*
* @return the underlying CDI thread
*/
protected ICDIThread getCDIThread() {
return fCDIThread;
}
/**
* 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.
*
* @see computeStackFrames()
*/
protected synchronized void preserveStackFrames() {
Iterator it = fStackFrames.iterator();
while( it.hasNext() ) {
CStackFrame frame = (CStackFrame)(((IAdaptable)it.next()).getAdapter( CStackFrame.class ));
if ( frame != null ) {
frame.preserve();
}
}
setRefreshChildren( true );
}
/**
* Disposes stack frames, to be completely re-computed on the next suspend event. This method should be called before this thread is resumed when stack
* frames are not to be re-used on the next suspend.
*/
protected synchronized void disposeStackFrames() {
Iterator it = fStackFrames.iterator();
while( it.hasNext() ) {
Object obj = it.next();
if ( obj instanceof CStackFrame ) {
((CStackFrame)obj).dispose();
}
}
fStackFrames.clear();
setLastStackDepth( 0 );
resetStatus();
setRefreshChildren( true );
}
protected void disposeStackFrames( int index, int length ) {
List removeList = new ArrayList( length );
Iterator it = fStackFrames.iterator();
int counter = 0;
while( it.hasNext() ) {
CStackFrame frame = (CStackFrame)(((IAdaptable)it.next()).getAdapter( CStackFrame.class ));
if ( frame != null && counter >= index && counter < index + length ) {
frame.dispose();
removeList.add( frame );
}
++counter;
}
fStackFrames.removeAll( removeList );
}
/**
* Notification this thread has terminated - update state and fire a terminate event.
*/
protected void terminated() {
setState( CDebugElementState.TERMINATED );
dispose();
}
private void handleSuspendedEvent( ICDISuspendedEvent event ) {
if ( !(getState().equals( CDebugElementState.RESUMED ) ||
getState().equals( CDebugElementState.STEPPED ) ||
getState().equals( CDebugElementState.SUSPENDING )) )
return;
setState( CDebugElementState.SUSPENDED );
ICDISessionObject reason = event.getReason();
setCurrentStateInfo( reason );
if ( reason instanceof ICDIEndSteppingRange ) {
handleEndSteppingRange( (ICDIEndSteppingRange)reason );
}
else if ( reason instanceof ICDIBreakpointHit ) {
handleBreakpointHit( (ICDIBreakpointHit)reason );
}
else if ( reason instanceof ICDISignalReceived ) {
handleSuspendedBySignal( (ICDISignalReceived)reason );
}
else {
// fireSuspendEvent( DebugEvent.CLIENT_REQUEST );
// Temporary fix for bug 56520
fireSuspendEvent( DebugEvent.BREAKPOINT );
}
}
private void handleResumedEvent( ICDIResumedEvent event ) {
CDebugElementState state = CDebugElementState.RESUMED;
int detail = DebugEvent.RESUME;
syncWithBackend();
if ( isCurrent() && event.getType() != ICDIResumedEvent.CONTINUE ) {
preserveStackFrames();
switch( event.getType() ) {
case ICDIResumedEvent.STEP_INTO:
case ICDIResumedEvent.STEP_INTO_INSTRUCTION:
detail = DebugEvent.STEP_INTO;
break;
case ICDIResumedEvent.STEP_OVER:
case ICDIResumedEvent.STEP_OVER_INSTRUCTION:
detail = DebugEvent.STEP_OVER;
break;
case ICDIResumedEvent.STEP_RETURN:
detail = DebugEvent.STEP_RETURN;
break;
}
state = CDebugElementState.STEPPED;
}
else {
disposeStackFrames();
fireChangeEvent( DebugEvent.CONTENT );
}
setCurrent( false );
setState( state );
setCurrentStateInfo( null );
fireResumeEvent( detail );
}
private void handleEndSteppingRange( ICDIEndSteppingRange endSteppingRange ) {
fireSuspendEvent( DebugEvent.STEP_END );
}
private void handleBreakpointHit( ICDIBreakpointHit breakpointHit ) {
IBreakpoint platformBreakpoint = ((CDebugTarget)getDebugTarget()).getBreakpointManager().getBreakpoint(breakpointHit.getBreakpoint());
if (platformBreakpoint != null)
CDebugCorePlugin.getDefault().getBreakpointActionManager().executeActions(platformBreakpoint, this);
fireSuspendEvent( DebugEvent.BREAKPOINT );
}
private void handleWatchpointHit( ICDIWatchpointTrigger watchPointTrigger ) {
IBreakpoint platformBreakpoint = ((CDebugTarget)getDebugTarget()).getBreakpointManager().getBreakpoint(watchPointTrigger.getWatchpoint());
if (platformBreakpoint != null)
CDebugCorePlugin.getDefault().getBreakpointActionManager().executeActions(platformBreakpoint, this);
fireSuspendEvent( DebugEvent.BREAKPOINT );
}
private void handleSuspendedBySignal( ICDISignalReceived signal ) {
fireSuspendEvent( DebugEvent.CLIENT_REQUEST );
}
private void handleTerminatedEvent( ICDIDestroyedEvent event ) {
setState( CDebugElementState.TERMINATED );
setCurrentStateInfo( null );
terminated();
}
private void handleDisconnectedEvent( ICDIDisconnectedEvent event ) {
setState( CDebugElementState.TERMINATED );
setCurrentStateInfo( null );
terminated();
}
private void handleChangedEvent( ICDIChangedEvent event ) {
}
/**
* Cleans up the internal state of this thread.
*/
protected void cleanup() {
getCDISession().getEventManager().removeEventListener( this );
disposeStackFrames();
final ICDIThread cdiThread = getCDIThread();
setCDIThread(null);
if (cdiThread instanceof ICDIDisposable) {
((ICDIDisposable)cdiThread).dispose();
}
}
private void setRefreshChildren( boolean refresh ) {
fRefreshChildren = refresh;
}
private boolean refreshChildren() {
return fRefreshChildren;
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.core.model.IRestart#canRestart()
*/
public boolean canRestart() {
return getDebugTarget() instanceof IRestart && ((IRestart)getDebugTarget()).canRestart();
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.core.model.IRestart#restart()
*/
public void restart() throws DebugException {
if ( canRestart() ) {
((IRestart)getDebugTarget()).restart();
}
}
protected boolean isCurrent() {
return fIsCurrent;
}
protected void setCurrent( boolean current ) {
boolean c = current;
if ( !c ) {
if ( getCDITarget().getConfiguration() instanceof ICDITargetConfiguration2
&& ((ICDITargetConfiguration2)getCDITarget().getConfiguration()).supportsThreadControl() )
c = true; // When the debugger can control individual
// threads treat every thread is "current"
}
fIsCurrent = c;
}
protected int getStackDepth() throws DebugException {
int depth = 0;
try {
final ICDIThread cdiThread = getCDIThread();
if (cdiThread != null) {
depth = cdiThread.getStackFrameCount();
}
}
catch( CDIException e ) {
setStatus( ICDebugElementStatus.WARNING, MessageFormat.format( CoreModelMessages.getString( "CThread.1" ), new String[]{ e.getMessage() } ) ); //$NON-NLS-1$
}
return depth;
}
protected int getMaxStackDepth() {
return MAX_STACK_DEPTH;
}
private void setLastStackDepth( int depth ) {
fLastStackDepth = depth;
}
protected int getLastStackDepth() {
return fLastStackDepth;
}
/* (non-Javadoc)
* @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class)
*/
public Object getAdapter( Class adapter ) {
if ( adapter.equals( IRunToLine.class ) ||
adapter.equals( IRunToAddress.class ) ||
adapter.equals( IResumeAtLine.class ) ||
adapter.equals( IResumeAtAddress.class ) ||
adapter.equals( IMoveToLine.class ) ||
adapter.equals( IMoveToAddress.class ) ) {
try {
// Alain: Put a proper fix later.
Object obj = getTopStackFrame();
if (obj instanceof ICStackFrame) {
return obj;
}
}
catch( DebugException e ) {
// do nothing
}
}
if ( adapter.equals( CDebugElementState.class ) )
return this;
if ( adapter == ICStackFrame.class ) {
try {
// Alain: Put a proper fix later.
Object obj = getTopStackFrame();
if (obj instanceof ICStackFrame) {
return obj;
}
}
catch( DebugException e ) {
// do nothing
}
}
if ( adapter == IMemoryBlockRetrieval.class ) {
return getDebugTarget().getAdapter( adapter );
}
if ( adapter == ICDIThread.class ) {
return getCDIThread();
}
return super.getAdapter( adapter );
}
protected void dispose() {
fDisposed = true;
cleanup();
}
protected boolean isDisposed() {
return fDisposed;
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.core.model.IResumeWithoutSignal#canResumeWithoutSignal()
*/
public boolean canResumeWithoutSignal() {
return (getDebugTarget() instanceof IResumeWithoutSignal && ((IResumeWithoutSignal)getDebugTarget()).canResumeWithoutSignal());
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.core.model.IResumeWithoutSignal#resumeWithoutSignal()
*/
public void resumeWithoutSignal() throws DebugException {
if ( canResumeWithoutSignal() ) {
((IResumeWithoutSignal)getDebugTarget()).resumeWithoutSignal();
}
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
public String toString() {
String result = ""; //$NON-NLS-1$
try {
result = getName();
}
catch( DebugException e ) {
}
return result;
}
protected void resumedByTarget( int detail, List events ) {
syncWithBackend();
if ( isCurrent() && detail != DebugEvent.CLIENT_REQUEST && detail != DebugEvent.UNSPECIFIED ) {
setState( CDebugElementState.STEPPED );
preserveStackFrames();
events.add( createResumeEvent( detail ) );
}
else {
setState( CDebugElementState.RESUMED );
disposeStackFrames();
// events.add( createResumeEvent( DebugEvent.CLIENT_REQUEST ) ); FIX FOR 218260
}
setCurrent( false );
setCurrentStateInfo( null );
}
protected boolean isInstructionsteppingEnabled() {
return ((CDebugTarget)getDebugTarget()).isInstructionSteppingEnabled();
}
protected void suspendByTarget( ICDISessionObject reason, ICDIThread suspensionThread ) {
setState( CDebugElementState.SUSPENDED );
setCurrentStateInfo( null );
final ICDIThread cdiThread = getCDIThread();
if ( cdiThread != null && cdiThread.equals( suspensionThread ) ) {
setCurrent( true );
setCurrentStateInfo( reason );
if ( reason instanceof ICDIEndSteppingRange ) {
handleEndSteppingRange( (ICDIEndSteppingRange)reason );
}
else if ( reason instanceof ICDIBreakpointHit ) {
handleBreakpointHit( (ICDIBreakpointHit)reason );
}
else if ( reason instanceof ICDIWatchpointTrigger ) {
handleWatchpointHit( (ICDIWatchpointTrigger)reason );
}
else if ( reason instanceof ICDISignalReceived ) {
handleSuspendedBySignal( (ICDISignalReceived)reason );
}
else {
// fireSuspendEvent( DebugEvent.CLIENT_REQUEST );
// Temporary fix for bug 56520
fireSuspendEvent( DebugEvent.BREAKPOINT );
}
}
}
private void syncWithBackend() {
ICDIThread cdiThread = getCDIThread();
if (cdiThread == null) {
return;
}
ICDIThread currentThread = null;
try {
currentThread = cdiThread.getTarget().getCurrentThread();
}
catch( CDIException e ) {
// ignore
}
setCurrent( cdiThread.equals( currentThread ) );
}
}