1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-06-08 10:16:03 +02:00

Bug 458091 - Debug frames - cache

- Implemented cache for stack trace (replaces original stack depth
cache with a cache of frames and depth)
 - This is mostly done to mitigate bugs in gdb but it will also help
speed up the debugger since the mi cache doesn't cache commands with
different limits and does not know that if we requested frames already
we don't need to ask about stack depth anymore
 - In case of unrecoverable errors I will also return some constant
depth like 5, later the front end will ask to fill it up and in many
cases gdb will be happy to do that, even if it failed to report the
proper depth in the first place. We originally just returned 1 but it is
really not enough for most of the cases. This will result in potential
white gaps at the end of trace, but it is better then showing just the
first frame when we have errors

Change-Id: I24b42fd7ffea082e8064a9c5348fd95c5f7777be
Signed-off-by: Alena Laskavaia <elaskavaia.cdt@gmail.com>
This commit is contained in:
Alena Laskavaia 2015-01-29 14:28:04 -05:00
parent 59854a48cd
commit d41b69136d

View file

@ -9,6 +9,7 @@
* Wind River Systems - initial API and implementation * Wind River Systems - initial API and implementation
* Ericsson - Modified for handling of multiple execution contexts * Ericsson - Modified for handling of multiple execution contexts
* Marc Khouzam (Ericsson) - Show return value of the method when doing a step-return (Bug 341731) * 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; package org.eclipse.cdt.dsf.mi.service;
@ -64,8 +65,9 @@ import org.eclipse.core.runtime.Status;
import org.osgi.framework.BundleContext; import org.osgi.framework.BundleContext;
public class MIStack extends AbstractDsfService 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 protected static class MIFrameDMC extends AbstractDMContext
implements IFrameDMContext implements IFrameDMContext
{ {
@ -135,37 +137,6 @@ implements IStack, ICachingService
return baseToString() + ".variable(" + fType + ")[" + fIndex + "]"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ 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<V,T> extends HashMap<V,T> {
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 * 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. * provide the data. Sub-classes must supply the MIArg object.
@ -196,13 +167,136 @@ implements IStack, ICachingService
private CommandCache fMICommandCache; private CommandCache fMICommandCache;
private CommandFactory fCommandFactory; 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<FrameData> frames = new ArrayList<FrameData>();
/**
* 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<Integer, FramesCacheInfo> {
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 // Two commands such as
// -stack-info-depth 11 // -stack-info-depth 11
// -stack-info-depth 2 // -stack-info-depth 2
// would both be sent to GDB because the command cache sees them as different. // 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 // This stackDepthCache allows us to know that if we already ask for a stack depth
// we can potentially re-use the answer. // we can potentially re-use the answer.
private StackDepthHashMap<Integer, StackDepthInfo> fStackDepthCache = new StackDepthHashMap<Integer, StackDepthInfo>(); private FramesCache fFramesCache = new FramesCache();
private MIStoppedEvent fCachedStoppedEvent; private MIStoppedEvent fCachedStoppedEvent;
private IRunControl fRunControl; private IRunControl fRunControl;
@ -220,11 +314,69 @@ implements IStack, ICachingService
*/ */
private Map<IMIExecutionDMContext, VariableData> fThreadToReturnVariable = new HashMap<IMIExecutionDMContext, VariableData>(); private Map<IMIExecutionDMContext, VariableData> fThreadToReturnVariable = new HashMap<IMIExecutionDMContext, VariableData>();
public MIStack(DsfSession session) public MIStack(DsfSession session) {
{
super(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 @Override
protected BundleContext getBundleContext() { protected BundleContext getBundleContext() {
return GdbPlugin.getBundleContext(); return GdbPlugin.getBundleContext();
@ -329,27 +481,69 @@ implements IStack, ICachingService
} }
} }
final ICommand<MIStackListFramesInfo> miStackListCmd; // if requested stack limit is bigger then currently cached this call will return -1
// firstIndex is the first index retrieved int depth = fFramesCache.getThreadFramesCache(execDmc.getThreadId()).getStackDepth(
final int firstIndex; endIndex > 0 ? endIndex + 1 : -1);
if (endIndex >= 0) { if (depth > 0) { // our stack depth cache is good so we can use it to fill levels array
miStackListCmd = fCommandFactory.createMIStackListFrames(execDmc, startIndex, endIndex); rm.setData(getDMFrames(execDmc, startIndex, endIndex));
firstIndex = startIndex; rm.done();
} else { return;
miStackListCmd = fCommandFactory.createMIStackListFrames(execDmc);
firstIndex = 0;
} }
fMICommandCache.execute( fMICommandCache.execute(
miStackListCmd, createMIStackListFrames(execDmc, startIndex, endIndex), //
new DataRequestMonitor<MIStackListFramesInfo>(getExecutor(), rm) { new DataRequestMonitor<MIStackListFramesInfo>(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 @Override
protected void handleSuccess() { protected void handleSuccess() {
rm.setData(getFrames(execDmc, getData(), firstIndex, endIndex, startIndex)); fFramesCache.update(execDmc.getThreadId(), getData());
rm.done(); 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<MIStackListFramesInfo> createMIStackListFrames(final IMIExecutionDMContext execDmc) {
return fCommandFactory.createMIStackListFrames(execDmc);
}
private ICommand<MIStackListFramesInfo> createMIStackListFrames(final IMIExecutionDMContext execDmc,
final int startIndex, final int endIndex) {
final ICommand<MIStackListFramesInfo> miStackListCmd;
if (endIndex >= 0) {
miStackListCmd = fCommandFactory.createMIStackListFrames(execDmc, startIndex, endIndex);
} else {
miStackListCmd = fCommandFactory.createMIStackListFrames(execDmc);
}
return miStackListCmd;
}
@Override @Override
public void getTopFrame(final IDMContext ctx, final DataRequestMonitor<IFrameDMContext> rm) { public void getTopFrame(final IDMContext ctx, final DataRequestMonitor<IFrameDMContext> rm) {
final IMIExecutionDMContext execDmc = DMContexts.getAncestorOfType(ctx, IMIExecutionDMContext.class); 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 @Override
public void getFrameData(final IFrameDMContext frameDmc, final DataRequestMonitor<IFrameDMData> rm) { public void getFrameData(final IFrameDMContext frameDmc, final DataRequestMonitor<IFrameDMData> rm) {
if (!(frameDmc instanceof MIFrameDMC)) { if (!(frameDmc instanceof MIFrameDMC)) {
@ -421,122 +595,66 @@ implements IStack, ICachingService
return; return;
} }
/** final int threadId = execDmc.getThreadId();
* Base class for the IFrameDMData object that uses an MIFrame object to final int frameLevel = miFrameDmc.fLevel;
* provide the data. Sub-classes must provide the MIFrame object FrameData fd = fFramesCache.getThreadFramesCache(threadId).getFrameData(frameLevel);
*/ if (fd != null) {
abstract class FrameData implements IFrameDMData rm.setData(fd);
{ rm.done();
abstract protected MIFrame getMIFrame(); return;
@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(); }
} }
// If requested frame is the top stack frame, try to retrieve it from // If requested frame is the top stack frame, try to retrieve it from
// the stopped event data. // 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 // Retrieve the top stack frame from the stopped event only if the selected thread is the one on which stopped event
// is raised // is raised
if (miFrameDmc.fLevel == 0) { if (frameLevel == 0) {
if (fCachedStoppedEvent != null && fCachedStoppedEvent.getFrame() != null && if (fCachedStoppedEvent != null && fCachedStoppedEvent.getFrame() != null
(execDmc.equals(fCachedStoppedEvent.getDMContext()) || fTraceVisualization)) && (execDmc.equals(fCachedStoppedEvent.getDMContext()) || fTraceVisualization)) {
{ try {
rm.setData(new FrameDataFromStoppedEvent(fCachedStoppedEvent)); rm.setData(new FrameDataFromStoppedEvent(fCachedStoppedEvent));
rm.done();
return; return;
} finally {
rm.done();
}
} }
} }
// If not, retrieve the full list of frame data. // 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( fMICommandCache.execute(
fCommandFactory.createMIStackListFrames(execDmc), createMIStackListFrames(execDmc),
new DataRequestMonitor<MIStackListFramesInfo>(getExecutor(), rm) { new DataRequestMonitor<MIStackListFramesInfo>(getExecutor(), rm) {
@Override @Override
protected void handleSuccess() { protected void handleSuccess() {
// Find the index to the correct MI frame object. FramesCacheInfo info = fFramesCache.update(threadId, getData());
int idx = findFrameIndex(getData().getMIFrames(), miFrameDmc.fLevel); FrameData frameData = info.getFrameData(frameLevel);
if (idx == -1) { if (frameData == null) {
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, rm.done(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE,
"Invalid frame " + frameDmc, null)); //$NON-NLS-1$ "Invalid frame " + frameDmc, null)); //$NON-NLS-1$
rm.done(); } else {
return; rm.done(frameData);
} }
// Create the data object.
rm.setData(new FrameDataFromMIStackFrameListInfo(getData(), idx));
rm.done();
} }
@Override @Override
protected void handleError() { protected void handleError() {
// We're seeing gdb in some cases fail when it's // We're seeing gdb in some cases fail when it's being asked for the stack
// being asked for the stack depth or stack // frames with no limits, but the same command succeeds if the request is limited
// frames, but the same command succeeds if // to one frame. So try again with a limit of 1.
// the request is limited to one frame. So try // It's better to show just one frame than none at all
// 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
fMICommandCache.execute( fMICommandCache.execute(
fCommandFactory.createMIStackListFrames(execDmc, miFrameDmc.fLevel, miFrameDmc.fLevel), createMIStackListFrames(execDmc, frameLevel, frameLevel),
new DataRequestMonitor<MIStackListFramesInfo>(getExecutor(), rm) { new DataRequestMonitor<MIStackListFramesInfo>(getExecutor(), rm) {
@Override @Override
protected void handleSuccess() { protected void handleSuccess() {
// Find the index to the correct MI frame object. FrameData frameData = fFramesCache.update(threadId, getData()).getFrameData(frameLevel);
int idx = findFrameIndex(getData().getMIFrames(), miFrameDmc.fLevel); if (frameData == null) {
if (idx == -1) { rm.done(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID,
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, INVALID_HANDLE, "Invalid frame " + frameDmc, null)); //$NON-NLS-1$
"Invalid frame " + frameDmc, null)); //$NON-NLS-1$ } else {
rm.done(); rm.done(frameData);
return;
} }
// 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]); 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. * Retrieves variables which are used to store the return values of functions.
*/ */
@ -899,18 +1009,16 @@ implements IStack, ICachingService
return; return;
} }
final int threadId = execDmc.getThreadId();
// Check our internal cache first because different commands can // Check our internal cache first because different commands can
// still be re-used. // still be re-used.
StackDepthInfo cachedDepth = fStackDepthCache.get(execDmc.getThreadId()); int depth = fFramesCache.getThreadFramesCache(threadId).getStackDepth(maxDepth);
if (cachedDepth != null) { if (depth > 0) {
if (cachedDepth.maxDepthRequested == 0 || rm.setData(depth);
(maxDepth != 0 && cachedDepth.maxDepthRequested >= maxDepth))
{
rm.setData(cachedDepth.returnedDepth);
rm.done(); rm.done();
return; return;
} }
}
ICommand<MIStackInfoDepthInfo> depthCommand = null; ICommand<MIStackInfoDepthInfo> depthCommand = null;
if (maxDepth > 0) { if (maxDepth > 0) {
@ -925,9 +1033,9 @@ implements IStack, ICachingService
@Override @Override
protected void handleSuccess() { protected void handleSuccess() {
// Store result in our internal cache // Store result in our internal cache
fStackDepthCache.put(execDmc.getThreadId(), new StackDepthInfo(maxDepth, getData().getDepth())); int stackDepth = getData().getDepth();
fFramesCache.update(threadId, stackDepth, maxDepth);
rm.setData(getData().getDepth()); rm.setData(stackDepth);
rm.done(); rm.done();
} }
@Override @Override
@ -945,47 +1053,26 @@ implements IStack, ICachingService
rm.setData(1); rm.setData(1);
rm.done(); rm.done();
} else { } else {
// We're seeing gdb in some cases fail when it's // gdb fails when being asked for the stack depth but stack frames command succeeds
// 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 // it seems like an overkill but it will cached and ui later will ask for it anyway
ICommand<MIStackListFramesInfo> listFramesCommand; fMICommandCache.execute(createMIStackListFrames(execDmc, 0, maxDepth - 1),
if (maxDepth <= 0) {
listFramesCommand = fCommandFactory.createMIStackListFrames(execDmc);
} else {
listFramesCommand = fCommandFactory.createMIStackListFrames(execDmc, 0, maxDepth - 1);
}
fMICommandCache.execute(
listFramesCommand,
new DataRequestMonitor<MIStackListFramesInfo>(getExecutor(), rm) { new DataRequestMonitor<MIStackListFramesInfo>(getExecutor(), rm) {
@Override @Override
protected void handleSuccess() { protected void handleSuccess() {
try { FramesCacheInfo info = fFramesCache.update(threadId, getData());
// Find the maximum level in returned frames int depth = info.getValidStackDepth();
MIFrame[] miFrames = getData().getMIFrames(); fFramesCache.update(threadId, depth, maxDepth); // update maxDepth for stack depth cache
int level = 0; rm.done(depth);
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 @Override
protected void handleError() { protected void handleError() {
// There is no point asking for stack-depth with limit of 1, lets just assume it is // Lets return that we have 5 frames, if we return just 1 front end will never ask
// at least 1, worst case it will show error or no data on the first frame // for more. There is chance that gdb will actually return correct frames later
rm.setData(1); // and one frame is not enough in many case to debug anything
rm.done(); rm.done(fFramesCache.getThreadFramesCache(threadId).getValidStackDepth());
}; };
} });
);
} }
} }
}); });
@ -1005,7 +1092,7 @@ implements IStack, ICachingService
if (e.getReason() != StateChangeReason.STEP) { if (e.getReason() != StateChangeReason.STEP) {
fCachedStoppedEvent = null; fCachedStoppedEvent = null;
fMICommandCache.reset(); fMICommandCache.reset();
fStackDepthCache.clear(); fFramesCache.clear();
} }
handleReturnValues(e); handleReturnValues(e);
@ -1041,7 +1128,7 @@ implements IStack, ICachingService
public void eventDispatched(ISuspendedDMEvent e) { public void eventDispatched(ISuspendedDMEvent e) {
fMICommandCache.setContextAvailable(e.getDMContext(), true); fMICommandCache.setContextAvailable(e.getDMContext(), true);
fMICommandCache.reset(); fMICommandCache.reset();
fStackDepthCache.clear(); fFramesCache.clear();
handleReturnValues(e); handleReturnValues(e);
} }
@ -1116,7 +1203,7 @@ implements IStack, ICachingService
@Override @Override
public void flushCache(IDMContext context) { public void flushCache(IDMContext context) {
fMICommandCache.reset(context); fMICommandCache.reset(context);
fStackDepthCache.clear(context); fFramesCache.clear(context);
fCachedStoppedEvent = null; fCachedStoppedEvent = null;
} }