diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/MIStack.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/MIStack.java index f0a3f076193..8b928b2d51c 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/MIStack.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/MIStack.java @@ -9,6 +9,7 @@ * Wind River Systems - initial API and implementation * Ericsson - Modified for handling of multiple execution contexts * Marc Khouzam (Ericsson) - Show return value of the method when doing a step-return (Bug 341731) + * Elena Laskavaia (Qnx Software Systems) - Stack Frames cache and error recovery *******************************************************************************/ package org.eclipse.cdt.dsf.mi.service; @@ -64,8 +65,9 @@ import org.eclipse.core.runtime.Status; import org.osgi.framework.BundleContext; public class MIStack extends AbstractDsfService -implements IStack, ICachingService -{ +implements IStack, ICachingService { + private static final int DEFAULT_STACK_DEPTH = 5; + protected static class MIFrameDMC extends AbstractDMContext implements IFrameDMContext { @@ -135,37 +137,6 @@ implements IStack, ICachingService return baseToString() + ".variable(" + fType + ")[" + fIndex + "]"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ } } - - /** - * Class to track stack depth requests for our internal cache - */ - private class StackDepthInfo { - // The maximum depth we requested - public int maxDepthRequested; - // The actual depth we received - public int returnedDepth; - - StackDepthInfo(int requested, int returned) { - maxDepthRequested = requested; - returnedDepth = returned; - } - } - - /** - * A HashMap for our StackDepth cache, that can clear based on a context. - */ - @SuppressWarnings("serial") - private class StackDepthHashMap extends HashMap { - public void clear(IDMContext context) { - final IMIExecutionDMContext execDmc = DMContexts.getAncestorOfType(context, IMIExecutionDMContext.class); - if (execDmc != null) { - remove(execDmc.getThreadId()); - } else { - clear(); - }; - } - } - /** * Same as with frame objects, this is a base class for the IVariableDMData object that uses an MIArg object to * provide the data. Sub-classes must supply the MIArg object. @@ -196,13 +167,136 @@ implements IStack, ICachingService private CommandCache fMICommandCache; private CommandFactory fCommandFactory; + /** + * Class to track stack depth requests for our internal cache + */ + private class FramesCacheInfo { + // If this set to true our knowledge of stack depths is limited to current depth, i.e + // we only know that stack depth is at least "stackDepth" but it could be more + private boolean limited = true; + // The actual depth we received + private int stackDepth = -1; + private final ArrayList frames = new ArrayList(); + + /** + * Return currently cached stack depth if cache value if valid, otherwise return -1. + * + * Cache value is valid if previous limited requests were with limits of this request. + * + * @param maxDepth + * @return + */ + public int getStackDepth(int maxDepth) { + if (!limited) + return stackDepth; + if (maxDepth > 0 && stackDepth >= maxDepth) + return stackDepth; + return -1; + } + + public void setStackDepth(int returned, int requested) { + if (returned <= 0) + return; // no valid depths, not updating + if (returned < requested) { + // since we did not reach the limit, cache is valid for unlimited range + limited = false; + } else if (requested <= 0) { + // if it was unlimited now stackDepth cache is valid in all ranges + limited = false; + } + if (returned > stackDepth) { + // that should only increase (and only if increased requested depth) + stackDepth = returned; + } + } + + /** + * Return currently cached stack depth, and if not cache available return default depth + * + * @return + */ + public int getValidStackDepth() { + if (stackDepth <= 0) + return DEFAULT_STACK_DEPTH; + return stackDepth; + } + + public void updateFrameData(FrameData frame) { + try { + int level = frame.getMIFrame().getLevel(); + if (stackDepth < level + 1) { + stackDepth = level + 1; + } + while (level >= frames.size()) { + frames.add(null); + } + frames.set(level, frame); + } catch (Exception e) { + // cannot afford throwing runtime exceptions + GdbPlugin.log(e); + } + } + + public FrameData getFrameData(int level) { + try { + if (level < 0 || level >= frames.size()) + return null; + return frames.get(level); + } catch (Exception e) { + // cannot afford throwing runtime exceptions + GdbPlugin.log(e); + } + return null; + } + } + + /** + * A HashMap for our StackDepth cache, that can clear based on a context. + */ + @SuppressWarnings("serial") + private class FramesCache extends HashMap { + public void clear(IDMContext context) { + final IMIExecutionDMContext execDmc = DMContexts.getAncestorOfType(context, IMIExecutionDMContext.class); + if (execDmc != null) { + remove(execDmc.getThreadId()); + } else { + clear(); + }; + } + + private FramesCacheInfo getThreadFramesCache(int threadId) { + FramesCacheInfo info = get(threadId); + if (info == null) { + put(threadId, info = new FramesCacheInfo()); + } + return info; + } + + public FramesCacheInfo update(int threadId, int stackDepth, int maxRequestedStackDepth) { + FramesCacheInfo info = getThreadFramesCache(threadId); + info.setStackDepth(stackDepth, maxRequestedStackDepth); + return info; + } + + public FramesCacheInfo update(int threadId, MIStackListFramesInfo framesInfo) { + FramesCacheInfo info = getThreadFramesCache(threadId); + if (framesInfo != null) { + int len = framesInfo.getMIFrames().length; + for (int i = 0; i < len; i++) { + info.updateFrameData(new FrameDataFromMIStackFrameListInfo(framesInfo, i)); + } + } + return info; + } + } + // Two commands such as // -stack-info-depth 11 // -stack-info-depth 2 // would both be sent to GDB because the command cache sees them as different. // This stackDepthCache allows us to know that if we already ask for a stack depth // we can potentially re-use the answer. - private StackDepthHashMap fStackDepthCache = new StackDepthHashMap(); + private FramesCache fFramesCache = new FramesCache(); private MIStoppedEvent fCachedStoppedEvent; private IRunControl fRunControl; @@ -220,11 +314,69 @@ implements IStack, ICachingService */ private Map fThreadToReturnVariable = new HashMap(); - public MIStack(DsfSession session) - { + public MIStack(DsfSession session) { super(session); } + /** + * Base class for the IFrameDMData object that uses an MIFrame object to + * provide the data. Sub-classes must provide the MIFrame object + */ + private abstract class FrameData implements IFrameDMData + { + abstract protected MIFrame getMIFrame(); + + @Override + public IAddress getAddress() { + String addr = getMIFrame().getAddress(); + if (addr == null || addr.length() == 0) + return new Addr32(0); + if (addr.startsWith("0x")) { //$NON-NLS-1$ + addr = addr.substring(2); + } + if (addr.length() <= 8) + return new Addr32(getMIFrame().getAddress()); + else + return new Addr64(getMIFrame().getAddress()); + } + + @Override + public int getColumn() { return 0; } + + @Override + public String getFile() { return getMIFrame().getFile(); } + @Override + public int getLine() { return getMIFrame().getLine(); } + @Override + public String getFunction() { return getMIFrame().getFunction(); } + @Override + public String getModule() { return ""; }//$NON-NLS-1$ + + @Override + public String toString() { return getMIFrame().toString(); } + } + + + private class FrameDataFromStoppedEvent extends FrameData { + private final MIStoppedEvent fEvent; + FrameDataFromStoppedEvent(MIStoppedEvent event) { fEvent = event; } + @Override + protected MIFrame getMIFrame() { return fEvent.getFrame(); } + } + + private class FrameDataFromMIStackFrameListInfo extends FrameData { + private MIStackListFramesInfo fFrameDataCacheInfo; + private int fFrameIndex; + + FrameDataFromMIStackFrameListInfo(MIStackListFramesInfo info, int index) { + fFrameDataCacheInfo = info; + fFrameIndex = index; + } + + @Override + protected MIFrame getMIFrame() { return fFrameDataCacheInfo.getMIFrames()[fFrameIndex]; } + } + @Override protected BundleContext getBundleContext() { return GdbPlugin.getBundleContext(); @@ -329,27 +481,69 @@ implements IStack, ICachingService } } - final ICommand miStackListCmd; - // firstIndex is the first index retrieved - final int firstIndex; - if (endIndex >= 0) { - miStackListCmd = fCommandFactory.createMIStackListFrames(execDmc, startIndex, endIndex); - firstIndex = startIndex; - } else { - miStackListCmd = fCommandFactory.createMIStackListFrames(execDmc); - firstIndex = 0; + // if requested stack limit is bigger then currently cached this call will return -1 + int depth = fFramesCache.getThreadFramesCache(execDmc.getThreadId()).getStackDepth( + endIndex > 0 ? endIndex + 1 : -1); + if (depth > 0) { // our stack depth cache is good so we can use it to fill levels array + rm.setData(getDMFrames(execDmc, startIndex, endIndex)); + rm.done(); + return; } + fMICommandCache.execute( - miStackListCmd, + createMIStackListFrames(execDmc, startIndex, endIndex), // new DataRequestMonitor(getExecutor(), rm) { + @Override + protected void handleError() { + // this command does not actually use frames but only return array of levels, + // lets just fake it based on know stack depth + rm.done(getDMFrames(execDmc, startIndex, endIndex, DEFAULT_STACK_DEPTH)); + } + @Override protected void handleSuccess() { - rm.setData(getFrames(execDmc, getData(), firstIndex, endIndex, startIndex)); - rm.done(); + fFramesCache.update(execDmc.getThreadId(), getData()); + rm.done(getDMFrames(execDmc, startIndex, endIndex)); } }); } + private IFrameDMContext[] getDMFrames(final IMIExecutionDMContext execDmc, int startIndex, + int endIndex, int stackDepth) { + if (endIndex > stackDepth - 1 || endIndex < 0) { + endIndex = stackDepth - 1; + } + if (startIndex > endIndex) + return new IFrameDMContext[] {}; + int length = endIndex - startIndex + 1; + IFrameDMContext[] frameDMCs = new MIFrameDMC[length]; + for (int i = 0; i < length; i++) { + frameDMCs[i] = createFrameDMContext(execDmc, i + startIndex); + } + return frameDMCs; + } + + private IFrameDMContext[] getDMFrames(final IMIExecutionDMContext execDmc, int startIndex, + int endIndex) { + int stackDepth = fFramesCache.getThreadFramesCache(execDmc.getThreadId()).getValidStackDepth(); + return getDMFrames(execDmc, startIndex, endIndex, stackDepth); + } + + private ICommand createMIStackListFrames(final IMIExecutionDMContext execDmc) { + return fCommandFactory.createMIStackListFrames(execDmc); + } + + private ICommand createMIStackListFrames(final IMIExecutionDMContext execDmc, + final int startIndex, final int endIndex) { + final ICommand miStackListCmd; + if (endIndex >= 0) { + miStackListCmd = fCommandFactory.createMIStackListFrames(execDmc, startIndex, endIndex); + } else { + miStackListCmd = fCommandFactory.createMIStackListFrames(execDmc); + } + return miStackListCmd; + } + @Override public void getTopFrame(final IDMContext ctx, final DataRequestMonitor rm) { final IMIExecutionDMContext execDmc = DMContexts.getAncestorOfType(ctx, IMIExecutionDMContext.class); @@ -384,26 +578,6 @@ implements IStack, ICachingService }); } - private IFrameDMContext[] getFrames(IMIExecutionDMContext execDmc, MIStackListFramesInfo info, int firstIndex, int lastIndex, int startIndex) { - int length = info.getMIFrames().length; - if (lastIndex > 0) { - int limit= lastIndex - startIndex + 1; - if (limit < length) { - length = limit; - } - } - IFrameDMContext[] frameDMCs = new MIFrameDMC[length]; - for (int i = 0; i < length; i++) { - //frameDMCs[i] = new MIFrameDMC(this, info.getMIFrames()[i].getLevel()); - final MIFrame frame= info.getMIFrames()[i + startIndex - firstIndex]; - assert startIndex + i == frame.getLevel(); - frameDMCs[i] = createFrameDMContext(execDmc, frame.getLevel()); - } - return frameDMCs; - } - - - @Override public void getFrameData(final IFrameDMContext frameDmc, final DataRequestMonitor rm) { if (!(frameDmc instanceof MIFrameDMC)) { @@ -421,122 +595,66 @@ implements IStack, ICachingService return; } - /** - * Base class for the IFrameDMData object that uses an MIFrame object to - * provide the data. Sub-classes must provide the MIFrame object - */ - abstract class FrameData implements IFrameDMData - { - abstract protected MIFrame getMIFrame(); - - @Override - public IAddress getAddress() { - String addr = getMIFrame().getAddress(); - if (addr == null || addr.length() == 0) - return new Addr32(0); - if (addr.startsWith("0x")) { //$NON-NLS-1$ - addr = addr.substring(2); - } - if (addr.length() <= 8) - return new Addr32(getMIFrame().getAddress()); - else - return new Addr64(getMIFrame().getAddress()); - } - - @Override - public int getColumn() { return 0; } - - @Override - public String getFile() { return getMIFrame().getFile(); } - @Override - public int getLine() { return getMIFrame().getLine(); } - @Override - public String getFunction() { return getMIFrame().getFunction(); } - @Override - public String getModule() { return ""; }//$NON-NLS-1$ - - @Override - public String toString() { return getMIFrame().toString(); } + final int threadId = execDmc.getThreadId(); + final int frameLevel = miFrameDmc.fLevel; + FrameData fd = fFramesCache.getThreadFramesCache(threadId).getFrameData(frameLevel); + if (fd != null) { + rm.setData(fd); + rm.done(); + return; } // If requested frame is the top stack frame, try to retrieve it from // the stopped event data. - class FrameDataFromStoppedEvent extends FrameData { - private final MIStoppedEvent fEvent; - FrameDataFromStoppedEvent(MIStoppedEvent event) { fEvent = event; } - @Override - protected MIFrame getMIFrame() { return fEvent.getFrame(); } - } // Retrieve the top stack frame from the stopped event only if the selected thread is the one on which stopped event // is raised - if (miFrameDmc.fLevel == 0) { - if (fCachedStoppedEvent != null && fCachedStoppedEvent.getFrame() != null && - (execDmc.equals(fCachedStoppedEvent.getDMContext()) || fTraceVisualization)) - { - rm.setData(new FrameDataFromStoppedEvent(fCachedStoppedEvent)); - rm.done(); - return; + if (frameLevel == 0) { + if (fCachedStoppedEvent != null && fCachedStoppedEvent.getFrame() != null + && (execDmc.equals(fCachedStoppedEvent.getDMContext()) || fTraceVisualization)) { + try { + rm.setData(new FrameDataFromStoppedEvent(fCachedStoppedEvent)); + return; + } finally { + rm.done(); + } } } // If not, retrieve the full list of frame data. - class FrameDataFromMIStackFrameListInfo extends FrameData { - private MIStackListFramesInfo fFrameDataCacheInfo; - private int fFrameIndex; - - FrameDataFromMIStackFrameListInfo(MIStackListFramesInfo info, int index) { - fFrameDataCacheInfo = info; - fFrameIndex = index; - } - - @Override - protected MIFrame getMIFrame() { return fFrameDataCacheInfo.getMIFrames()[fFrameIndex]; } - } - fMICommandCache.execute( - fCommandFactory.createMIStackListFrames(execDmc), + createMIStackListFrames(execDmc), new DataRequestMonitor(getExecutor(), rm) { @Override protected void handleSuccess() { - // Find the index to the correct MI frame object. - int idx = findFrameIndex(getData().getMIFrames(), miFrameDmc.fLevel); - if (idx == -1) { - rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, + FramesCacheInfo info = fFramesCache.update(threadId, getData()); + FrameData frameData = info.getFrameData(frameLevel); + if (frameData == null) { + rm.done(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, "Invalid frame " + frameDmc, null)); //$NON-NLS-1$ - rm.done(); - return; + } else { + rm.done(frameData); } - // Create the data object. - rm.setData(new FrameDataFromMIStackFrameListInfo(getData(), idx)); - rm.done(); } @Override protected void handleError() { - // We're seeing gdb in some cases fail when it's - // being asked for the stack depth or stack - // frames, but the same command succeeds if - // the request is limited to one frame. So try - // again for a specific frame. It's better to show - // just one frame than none at all. Since it is only happen on error - // this should not contribute much to increased traffic + // We're seeing gdb in some cases fail when it's being asked for the stack + // frames with no limits, but the same command succeeds if the request is limited + // to one frame. So try again with a limit of 1. + // It's better to show just one frame than none at all fMICommandCache.execute( - fCommandFactory.createMIStackListFrames(execDmc, miFrameDmc.fLevel, miFrameDmc.fLevel), + createMIStackListFrames(execDmc, frameLevel, frameLevel), new DataRequestMonitor(getExecutor(), rm) { @Override protected void handleSuccess() { - // Find the index to the correct MI frame object. - int idx = findFrameIndex(getData().getMIFrames(), miFrameDmc.fLevel); - if (idx == -1) { - rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, - "Invalid frame " + frameDmc, null)); //$NON-NLS-1$ - rm.done(); - return; + FrameData frameData = fFramesCache.update(threadId, getData()).getFrameData(frameLevel); + if (frameData == null) { + rm.done(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, + INVALID_HANDLE, "Invalid frame " + frameDmc, null)); //$NON-NLS-1$ + } else { + rm.done(frameData); } - // Create the data object. - rm.setData(new FrameDataFromMIStackFrameListInfo(getData(), idx)); - rm.done(); } }); } @@ -790,14 +908,6 @@ implements IStack, ICachingService return variableNames.values().toArray(new MIVariableDMC[0]); } - private int findFrameIndex(MIFrame[] frames, int level) { - for (int idx = 0; idx < frames.length; idx++) { - if (frames[idx].getLevel() == level) - return idx; - } - return -1; - } - /** * Retrieves variables which are used to store the return values of functions. */ @@ -831,61 +941,61 @@ implements IStack, ICachingService countingRm.setDoneCount(3); // First show any return values of methods - getReturnValues( - frameDmc, - new DataRequestMonitor(getExecutor(), countingRm) { - @Override - protected void handleSuccess() { - localsList.addAll( Arrays.asList(getData()) ); - countingRm.done(); - } - }); + getReturnValues( + frameDmc, + new DataRequestMonitor(getExecutor(), countingRm) { + @Override + protected void handleSuccess() { + localsList.addAll( Arrays.asList(getData()) ); + countingRm.done(); + } + }); - // Then show arguments - getArguments( - frameDmc, - new DataRequestMonitor(getExecutor(), countingRm) { - @Override - protected void handleSuccess() { - localsList.addAll( Arrays.asList(getData()) ); - countingRm.done(); - } - }); + // Then show arguments + getArguments( + frameDmc, + new DataRequestMonitor(getExecutor(), countingRm) { + @Override + protected void handleSuccess() { + localsList.addAll( Arrays.asList(getData()) ); + countingRm.done(); + } + }); - // Finally get the local variables - fMICommandCache.execute( - // We don't actually need to ask for the values in this case, but since - // we will ask for them right after, it is more efficient to ask for them now - // so as to cache the result. If the command fails, then we will ask for - // the result without the values - // Don't ask for value when we are visualizing trace data, since some - // data will not be there, and the command will fail - fCommandFactory.createMIStackListLocals(frameDmc, !fTraceVisualization), - new DataRequestMonitor(getExecutor(), countingRm) { - @Override - protected void handleSuccess() { - localsList.addAll( Arrays.asList( - makeVariableDMCs(frameDmc, MIVariableDMC.Type.LOCAL, getData().getLocals())) ); - countingRm.done(); - } - @Override - protected void handleError() { - // If the command fails it can be because we asked for values. - // This can happen with uninitialized values and pretty printers (bug 307614). - // Since asking for values was simply an optimization - // to store the command in the cache, let's retry the command without asking for values. - fMICommandCache.execute( - fCommandFactory.createMIStackListLocals(frameDmc, false), - new DataRequestMonitor(getExecutor(), countingRm) { - @Override - protected void handleSuccess() { - localsList.addAll( Arrays.asList( - makeVariableDMCs(frameDmc, MIVariableDMC.Type.LOCAL, getData().getLocals())) ); - countingRm.done(); - } - }); - } - }); + // Finally get the local variables + fMICommandCache.execute( + // We don't actually need to ask for the values in this case, but since + // we will ask for them right after, it is more efficient to ask for them now + // so as to cache the result. If the command fails, then we will ask for + // the result without the values + // Don't ask for value when we are visualizing trace data, since some + // data will not be there, and the command will fail + fCommandFactory.createMIStackListLocals(frameDmc, !fTraceVisualization), + new DataRequestMonitor(getExecutor(), countingRm) { + @Override + protected void handleSuccess() { + localsList.addAll( Arrays.asList( + makeVariableDMCs(frameDmc, MIVariableDMC.Type.LOCAL, getData().getLocals())) ); + countingRm.done(); + } + @Override + protected void handleError() { + // If the command fails it can be because we asked for values. + // This can happen with uninitialized values and pretty printers (bug 307614). + // Since asking for values was simply an optimization + // to store the command in the cache, let's retry the command without asking for values. + fMICommandCache.execute( + fCommandFactory.createMIStackListLocals(frameDmc, false), + new DataRequestMonitor(getExecutor(), countingRm) { + @Override + protected void handleSuccess() { + localsList.addAll( Arrays.asList( + makeVariableDMCs(frameDmc, MIVariableDMC.Type.LOCAL, getData().getLocals())) ); + countingRm.done(); + } + }); + } + }); } @Override @@ -899,17 +1009,15 @@ implements IStack, ICachingService return; } + final int threadId = execDmc.getThreadId(); + // Check our internal cache first because different commands can // still be re-used. - StackDepthInfo cachedDepth = fStackDepthCache.get(execDmc.getThreadId()); - if (cachedDepth != null) { - if (cachedDepth.maxDepthRequested == 0 || - (maxDepth != 0 && cachedDepth.maxDepthRequested >= maxDepth)) - { - rm.setData(cachedDepth.returnedDepth); - rm.done(); - return; - } + int depth = fFramesCache.getThreadFramesCache(threadId).getStackDepth(maxDepth); + if (depth > 0) { + rm.setData(depth); + rm.done(); + return; } ICommand depthCommand = null; @@ -925,9 +1033,9 @@ implements IStack, ICachingService @Override protected void handleSuccess() { // Store result in our internal cache - fStackDepthCache.put(execDmc.getThreadId(), new StackDepthInfo(maxDepth, getData().getDepth())); - - rm.setData(getData().getDepth()); + int stackDepth = getData().getDepth(); + fFramesCache.update(threadId, stackDepth, maxDepth); + rm.setData(stackDepth); rm.done(); } @Override @@ -945,47 +1053,26 @@ implements IStack, ICachingService rm.setData(1); rm.done(); } else { - // We're seeing gdb in some cases fail when it's - // being asked for the stack depth but stack frames command succeeds + // gdb fails when being asked for the stack depth but stack frames command succeeds // it seems like an overkill but it will cached and ui later will ask for it anyway - ICommand listFramesCommand; - if (maxDepth <= 0) { - listFramesCommand = fCommandFactory.createMIStackListFrames(execDmc); - } else { - listFramesCommand = fCommandFactory.createMIStackListFrames(execDmc, 0, maxDepth - 1); - } - fMICommandCache.execute( - listFramesCommand, + fMICommandCache.execute(createMIStackListFrames(execDmc, 0, maxDepth - 1), new DataRequestMonitor(getExecutor(), rm) { - @Override - protected void handleSuccess() { - try { - // Find the maximum level in returned frames - MIFrame[] miFrames = getData().getMIFrames(); - int level = 0; - for (MIFrame miFrame : miFrames) { - if (miFrame.getLevel() > level) { - level = miFrame.getLevel(); - } - } - // Create the data object. Depth is +1 of maximum frame level - int depth = level + 1; - fStackDepthCache.put(execDmc.getThreadId(), new StackDepthInfo(maxDepth, depth)); - rm.setData(depth); - } finally { - rm.done(); // we have to close monitor no matter what - } - } + @Override + protected void handleSuccess() { + FramesCacheInfo info = fFramesCache.update(threadId, getData()); + int depth = info.getValidStackDepth(); + fFramesCache.update(threadId, depth, maxDepth); // update maxDepth for stack depth cache + rm.done(depth); + } - @Override - protected void handleError() { - // There is no point asking for stack-depth with limit of 1, lets just assume it is - // at least 1, worst case it will show error or no data on the first frame - rm.setData(1); - rm.done(); - }; - } - ); + @Override + protected void handleError() { + // Lets return that we have 5 frames, if we return just 1 front end will never ask + // for more. There is chance that gdb will actually return correct frames later + // and one frame is not enough in many case to debug anything + rm.done(fFramesCache.getThreadFramesCache(threadId).getValidStackDepth()); + }; + }); } } }); @@ -1005,7 +1092,7 @@ implements IStack, ICachingService if (e.getReason() != StateChangeReason.STEP) { fCachedStoppedEvent = null; fMICommandCache.reset(); - fStackDepthCache.clear(); + fFramesCache.clear(); } handleReturnValues(e); @@ -1041,7 +1128,7 @@ implements IStack, ICachingService public void eventDispatched(ISuspendedDMEvent e) { fMICommandCache.setContextAvailable(e.getDMContext(), true); fMICommandCache.reset(); - fStackDepthCache.clear(); + fFramesCache.clear(); handleReturnValues(e); } @@ -1116,7 +1203,7 @@ implements IStack, ICachingService @Override public void flushCache(IDMContext context) { fMICommandCache.reset(context); - fStackDepthCache.clear(context); + fFramesCache.clear(context); fCachedStoppedEvent = null; }