From 2a935a9926ff84f71622e87c4d70bd0e5a2c9bad Mon Sep 17 00:00:00 2001 From: Marc Khouzam Date: Mon, 12 Aug 2013 08:21:06 -0400 Subject: [PATCH] Bug 341731 - Show values returned from function calls when doing a step-return operation Change-Id: I4ac5c64a940ffcbe75b21618a74f2c4eba93d27e Signed-off-by: Marc Khouzam Reviewed-on: https://git.eclipse.org/r/15377 Reviewed-by: Marc-Andre Laperle IP-Clean: Marc-Andre Laperle Tested-by: Marc-Andre Laperle Reviewed-by: Mikhail Khodjaiants IP-Clean: Mikhail Khodjaiants Tested-by: Mikhail Khodjaiants --- .../ui/viewmodel/GdbVariableVMNode.java | 30 ++ .../cdt/dsf/mi/service/MIExpressions.java | 195 ++++++++++- .../eclipse/cdt/dsf/mi/service/MIStack.java | 172 +++++++-- .../eclipse/cdt/dsf/mi/service/Messages.java | 1 + .../cdt/dsf/mi/service/Messages.properties | 1 + .../data/launch/src/ExpressionTestApp.cc | 43 ++- .../cdt/tests/dsf/gdb/framework/SyncUtil.java | 65 +++- .../dsf/gdb/tests/MIExpressionsTest.java | 325 ++++++++++-------- .../MIExpressionsNonStopTest_7_0.java | 31 ++ .../MIExpressionsNonStopTest_7_1.java | 31 ++ .../MIExpressionsNonStopTest_7_2.java | 31 ++ .../MIExpressionsNonStopTest_7_3.java | 31 ++ .../MIExpressionsNonStopTest_7_4.java | 31 ++ .../MIExpressionsNonStopTest_7_5.java | 31 ++ .../MIExpressionsNonStopTest_7_6.java | 31 ++ 15 files changed, 875 insertions(+), 174 deletions(-) create mode 100644 dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/tests_7_0/MIExpressionsNonStopTest_7_0.java create mode 100644 dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/tests_7_1/MIExpressionsNonStopTest_7_1.java create mode 100644 dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/tests_7_2/MIExpressionsNonStopTest_7_2.java create mode 100644 dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/tests_7_3/MIExpressionsNonStopTest_7_3.java create mode 100644 dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/tests_7_4/MIExpressionsNonStopTest_7_4.java create mode 100644 dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/tests_7_5/MIExpressionsNonStopTest_7_5.java create mode 100644 dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/tests_7_6/MIExpressionsNonStopTest_7_6.java diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/viewmodel/GdbVariableVMNode.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/viewmodel/GdbVariableVMNode.java index 036826a4833..751a152aa04 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/viewmodel/GdbVariableVMNode.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/viewmodel/GdbVariableVMNode.java @@ -9,6 +9,7 @@ * Freescale Semiconductor - initial API and implementation * Jens Elmenthaler (Verigy) - Added Full GDB pretty-printing support (bug 302121) * Marc Khouzam (Ericsson) - Add support disable "View Memory" action (bug 418710) + * Marc Khouzam (Ericsson) - Turn off "watch" action for return values of methods (bug 341731) *******************************************************************************/ package org.eclipse.cdt.dsf.gdb.internal.ui.viewmodel; @@ -48,6 +49,7 @@ import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenCountUpd import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate; import org.eclipse.debug.internal.ui.viewers.model.provisional.IHasChildrenUpdate; import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta; +import org.eclipse.debug.ui.actions.IWatchExpressionFactoryAdapter2; import org.eclipse.jface.util.PropertyChangeEvent; import org.eclipse.jface.viewers.TreePath; @@ -194,6 +196,15 @@ public class GdbVariableVMNode extends VariableVMNode { } return super.canViewInMemory(); } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + @Override + public Object getAdapter(Class adapter) { + if (adapter.isAssignableFrom(IWatchExpressionFactoryAdapter2.class)) { + return fGdbVariableExpressionFactory; + } + return super.getAdapter(adapter); + } }; private static boolean isConvenienceVariable(String expr) { @@ -229,6 +240,25 @@ public class GdbVariableVMNode extends VariableVMNode { return false; } + /** + * A factory to control the "Watch" action for GDB variables. + */ + protected class GdbVariableExpressionFactory extends VariableExpressionFactory { + @Override + public boolean canCreateWatchExpression(Object element) { + if (element instanceof VariableExpressionVMC) { + String expression = ((VariableExpressionVMC)element).getExpression(); + if (isConvenienceVariable(expression)) { + return false; + } + } + + return super.canCreateWatchExpression(element); + } + } + + final protected VariableExpressionFactory fGdbVariableExpressionFactory = new GdbVariableExpressionFactory(); + /** * The special context representing more children to be available. * diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/MIExpressions.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/MIExpressions.java index 5d9f4bfcef2..d307f4183f6 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/MIExpressions.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/MIExpressions.java @@ -10,13 +10,17 @@ * Ericsson - Modified for handling of multiple execution contexts * Axel Mueller - Bug 306555 - Add support for cast to type / view as array (IExpressions2) * Jens Elmenthaler (Verigy) - Added Full GDB pretty-printing support (bug 302121) + * Marc Khouzam (Ericsson) - Added support for expression aliases for return values of functions (bug 341731) *******************************************************************************/ package org.eclipse.cdt.dsf.mi.service; +import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.Hashtable; +import java.util.List; import java.util.Map; +import java.util.Map.Entry; import org.eclipse.cdt.core.IAddress; import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; @@ -35,7 +39,11 @@ import org.eclipse.cdt.dsf.debug.service.IFormattedValues; import org.eclipse.cdt.dsf.debug.service.IMemory.IMemoryChangedEvent; import org.eclipse.cdt.dsf.debug.service.IMemory.IMemoryDMContext; import org.eclipse.cdt.dsf.debug.service.IRegisters.IRegisterDMContext; -import org.eclipse.cdt.dsf.debug.service.IRunControl; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerSuspendedDMEvent; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IExitedDMEvent; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IResumedDMEvent; +import org.eclipse.cdt.dsf.debug.service.IRunControl.ISuspendedDMEvent; import org.eclipse.cdt.dsf.debug.service.IRunControl.StateChangeReason; import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMContext; import org.eclipse.cdt.dsf.debug.service.command.CommandCache; @@ -49,12 +57,16 @@ import org.eclipse.cdt.dsf.mi.service.command.commands.ExprMetaGetChildCount; import org.eclipse.cdt.dsf.mi.service.command.commands.ExprMetaGetChildren; import org.eclipse.cdt.dsf.mi.service.command.commands.ExprMetaGetValue; import org.eclipse.cdt.dsf.mi.service.command.commands.ExprMetaGetVar; +import org.eclipse.cdt.dsf.mi.service.command.events.IMIDMEvent; +import org.eclipse.cdt.dsf.mi.service.command.events.MIFunctionFinishedEvent; +import org.eclipse.cdt.dsf.mi.service.command.events.MIStoppedEvent; import org.eclipse.cdt.dsf.mi.service.command.output.ExprMetaGetAttributesInfo; import org.eclipse.cdt.dsf.mi.service.command.output.ExprMetaGetChildCountInfo; import org.eclipse.cdt.dsf.mi.service.command.output.ExprMetaGetChildrenInfo; import org.eclipse.cdt.dsf.mi.service.command.output.ExprMetaGetValueInfo; import org.eclipse.cdt.dsf.mi.service.command.output.ExprMetaGetVarInfo; import org.eclipse.cdt.dsf.mi.service.command.output.MIDataEvaluateExpressionInfo; +import org.eclipse.cdt.dsf.mi.service.command.output.MIFrame; import org.eclipse.cdt.dsf.service.AbstractDsfService; import org.eclipse.cdt.dsf.service.DsfServiceEventHandler; import org.eclipse.cdt.dsf.service.DsfServicesTracker; @@ -65,6 +77,8 @@ import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.osgi.framework.BundleContext; +import com.ibm.icu.text.MessageFormat; + /** * This class implements a debugger expression evaluator as a DSF service. The * primary interface that clients of this class should use is IExpressions. @@ -815,6 +829,116 @@ public class MIExpressions extends AbstractDsfService implements IMIExpressions, } } + /** + * Keeps track of aliases for return values of methods. + */ + private class ReturnValueAliasing { + /** + * Map of expression to alias. The expression is the name of the convenience variable + * storing the return value, e.g., $1 -> "foo() returned" + * This map allows to quickly find the alias to be used for return value variables. + */ + private Map fExpressionAliasesMap = new HashMap(); + /** + * Map of thread to aliases expression list. This map allows to know which aliases are related + * to a thread of execution. This is important to allow us to delete aliases when a + * thread exits. Note that we need a list because we keep all previous aliases until + * the thread exits. + */ + private Map> fThreadToAliasedExpressionsMap = new HashMap>(); + /** + * Map of thread to the name of the method the thread last stopped in. + * This allows us to create the alias based on the method the thread was in + * before it returned out of the method. + */ + private Map fThreadToTopMethodName = new HashMap(); + + /** + * Create an alias for expr with respect to threadDmc. + * The alias is created based on where threadDmc was previously stopped. + */ + public void createAlias(IMIExecutionDMContext threadDmc, String expr) { + String alias = expr; + String methodName = fThreadToTopMethodName.get(threadDmc); + if (methodName != null) { + alias = MessageFormat.format(Messages.MIExpressions_ReturnValueAlias, + methodName + "()"); //$NON-NLS-1$ + } + + fExpressionAliasesMap.put(expr, alias); + + List aliasedExprList = fThreadToAliasedExpressionsMap.get(threadDmc); + if (aliasedExprList == null) { + aliasedExprList = new ArrayList(); + fThreadToAliasedExpressionsMap.put(threadDmc, aliasedExprList); + } + aliasedExprList.add(expr); + } + + /** + * Clear all information related to a particular thread of execution. + */ + public void clearThread(IMIExecutionDMContext threadDmc) { + fThreadToTopMethodName.remove(threadDmc); + clearAliases(threadDmc); + } + + /** + * Clear all aliased expressions related to a particular thread of execution. + * It is good to keep the aliases around as long as the thread is alive; + * even if we won't show the return value automatically, the user + * could add the expression in the expression view, and the alias + * would then be used. + */ + public void clearAliases(IMIExecutionDMContext threadDmc) { + List aliasedExprList = fThreadToAliasedExpressionsMap.remove(threadDmc); + if (aliasedExprList != null) { + for (String expr : aliasedExprList) { + fExpressionAliasesMap.remove(expr); + } + } + } + + /** + * Update the method name of the last location where threadDmc was stopped. + */ + public void updateStoppedLocation(IMIExecutionDMContext threadDmc, String methodName) { + fThreadToTopMethodName.put(threadDmc, methodName); + } + + /** + * @return The alias for 'expr' if there is one. null if there + * is no alias for that expression. + */ + public String getAlias(String expr) { + String alias = fExpressionAliasesMap.get(expr); + if (alias == null) { + // Check if the expression contains the string that must be aliased. + // E.g., $1[0], *$2 + // If it does, just replace that string within the expression to + // create the full alias + for (Entry entry : fExpressionAliasesMap.entrySet()) { + int index = expr.indexOf(entry.getKey()); + if (index != -1) { + // Found the string! Now replace it with our alias. + // We put it between () to make things clearer to the user. + // Note that there can only be one string contained + // in the expression, so once we found it, we are done. + alias = expr.substring(0, index) + + "(" + entry.getValue() + ")" + //$NON-NLS-1$ //$NON-NLS-2$ + expr.substring(index + entry.getKey().length()); + break; + } + } + } + return alias; + } + } + + /** Structure to keep track of aliases for method return values. */ + private ReturnValueAliasing fReturnValueAliases = new ReturnValueAliasing(); + + /** * @since 4.3 */ @@ -1061,8 +1185,13 @@ public class MIExpressions extends AbstractDsfService implements IMIExpressions, } } + String relativeExpr = getData().getExpr(); + String alias = fReturnValueAliases.getAlias(relativeExpr); + if (alias != null) { + relativeExpr = alias; + } rm.setData(new ExpressionDMData( - getData().getExpr(),getData().getType(), getData().getNumChildren(), + relativeExpr, getData().getType(), getData().getNumChildren(), getData().getEditable(), basicType)); rm.done(); } @@ -1492,7 +1621,7 @@ public class MIExpressions extends AbstractDsfService implements IMIExpressions, } @DsfServiceEventHandler - public void eventDispatched(IRunControl.IResumedDMEvent e) { + public void eventDispatched(IResumedDMEvent e) { fExpressionCache.setContextAvailable(e.getDMContext(), false); if (e.getReason() != StateChangeReason.STEP) { fExpressionCache.reset(); @@ -1500,9 +1629,53 @@ public class MIExpressions extends AbstractDsfService implements IMIExpressions, } @DsfServiceEventHandler - public void eventDispatched(IRunControl.ISuspendedDMEvent e) { + public void eventDispatched(ISuspendedDMEvent e) { fExpressionCache.setContextAvailable(e.getDMContext(), true); fExpressionCache.reset(); + + handleReturnValueAliasing(e); + } + + private void handleReturnValueAliasing(ISuspendedDMEvent e) { + // Process MIStoppedEvent from within the ISuspendedDMEvent + // to avoid any race conditions where the actual MIStoppedEvent + // can arrive faster that a preceding IResumedDMEvent + if (e instanceof IMIDMEvent) { + Object miEvent = ((IMIDMEvent)e).getMIEvent(); + if (miEvent instanceof MIStoppedEvent) { + IMIExecutionDMContext stoppedEventThread = null; + if (e instanceof IContainerSuspendedDMEvent) { + // All-stop mode + IExecutionDMContext[] triggerContexts = ((IContainerSuspendedDMEvent)e).getTriggeringContexts(); + if (triggerContexts.length != 0 && triggerContexts[0] instanceof IMIExecutionDMContext) { + stoppedEventThread = (IMIExecutionDMContext)triggerContexts[0]; + } + } else { + // Non-stop mode + IDMContext dmc = e.getDMContext(); + if (dmc instanceof IMIExecutionDMContext) { + stoppedEventThread = (IMIExecutionDMContext)dmc; + } + } + + if (stoppedEventThread != null) { + if (miEvent instanceof MIFunctionFinishedEvent) { + // When getting an MIFunctionFinishedEvent we must set + // a proper alias for the convenience variable + String resultVar = ((MIFunctionFinishedEvent)miEvent).getGDBResultVar(); + fReturnValueAliases.createAlias(stoppedEventThread, resultVar); + } + + // Keep track of the latest method the thread is stopped in. + // Must do this after creating any alias, or else we will overwrite + // the previous function name, which we need for the alias + MIFrame frame = ((MIStoppedEvent)miEvent).getFrame(); + if (frame != null) { + fReturnValueAliases.updateStoppedLocation(stoppedEventThread, frame.getFunction()); + } + } + } + } } @DsfServiceEventHandler @@ -1520,6 +1693,20 @@ public class MIExpressions extends AbstractDsfService implements IMIExpressions, fTraceVisualization = false; } } + + /** + * @nooverride This method is not intended to be re-implemented or extended by clients. + * @noreference This method is not intended to be referenced by clients. + */ + @DsfServiceEventHandler + public void eventDispatched(IExitedDMEvent e) { + IDMContext ctx = e.getDMContext(); + if (ctx instanceof IMIExecutionDMContext) { + // When a thread exits, clear the alias structure for that + // thread to avoid leaks + fReturnValueAliases.clearThread((IMIExecutionDMContext)ctx); + } + } /** * {@inheritDoc} 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 c37fbc247eb..71644f5e1e2 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 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2006, 2010 Wind River Systems and others. + * Copyright (c) 2006, 2013 Wind River 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 @@ -8,6 +8,7 @@ * Contributors: * 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) *******************************************************************************/ package org.eclipse.cdt.dsf.mi.service; @@ -29,6 +30,9 @@ import org.eclipse.cdt.dsf.datamodel.DMContexts; import org.eclipse.cdt.dsf.datamodel.IDMContext; import org.eclipse.cdt.dsf.debug.service.ICachingService; import org.eclipse.cdt.dsf.debug.service.IRunControl; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerResumedDMEvent; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerSuspendedDMEvent; import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext; import org.eclipse.cdt.dsf.debug.service.IRunControl.IResumedDMEvent; import org.eclipse.cdt.dsf.debug.service.IRunControl.ISuspendedDMEvent; @@ -42,6 +46,7 @@ import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin; import org.eclipse.cdt.dsf.gdb.service.IGDBTraceControl.ITraceRecordSelectedChangedDMEvent; import org.eclipse.cdt.dsf.mi.service.command.CommandFactory; import org.eclipse.cdt.dsf.mi.service.command.events.IMIDMEvent; +import org.eclipse.cdt.dsf.mi.service.command.events.MIFunctionFinishedEvent; import org.eclipse.cdt.dsf.mi.service.command.events.MIStoppedEvent; import org.eclipse.cdt.dsf.mi.service.command.output.MIArg; import org.eclipse.cdt.dsf.mi.service.command.output.MIFrame; @@ -92,7 +97,7 @@ public class MIStack extends AbstractDsfService protected static class MIVariableDMC extends AbstractDMContext implements IVariableDMContext { - public enum Type { ARGUMENT, LOCAL } + public enum Type { ARGUMENT, LOCAL, /** @since 4.4 */RETURN_VALUES } final private Type fType; final private int fIndex; @@ -117,6 +122,7 @@ public class MIStack extends AbstractDsfService int typeFactor = 0; if (fType == Type.LOCAL) typeFactor = 2; else if (fType == Type.ARGUMENT) typeFactor = 3; + else if (fType == Type.RETURN_VALUES) typeFactor = 4; return super.baseHashCode() ^ typeFactor ^ fIndex; } @@ -155,6 +161,33 @@ public class MIStack extends AbstractDsfService }; } } + + /** + * 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. + */ + private class VariableData implements IVariableDMData { + private MIArg fMIArg; + + public VariableData(MIArg arg){ + fMIArg = arg; + } + + @Override + public String getName() { + return fMIArg.getName(); + } + + @Override + public String getValue() { + return fMIArg.getValue(); + } + + @Override + public String toString() { + return fMIArg.toString(); + } + } private CommandCache fMICommandCache; private CommandFactory fCommandFactory; @@ -176,6 +209,13 @@ public class MIStack extends AbstractDsfService */ private boolean fTraceVisualization; + /** + * A Map of a return value for each thread. + * A return value is stored when the user performs a step-return, + * and it cleared as soon as that thread executes again. + */ + private Map fThreadToReturnVariable = new HashMap(); + public MIStack(DsfSession session) { super(session); @@ -614,23 +654,6 @@ public class MIStack extends AbstractDsfService return; } - /** - * 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. - */ - class VariableData implements IVariableDMData { - private MIArg dsfMIArg; - VariableData(MIArg arg){ - dsfMIArg = arg; - } - @Override - public String getName() { return dsfMIArg.getName(); } - @Override - public String getValue() { return dsfMIArg.getValue(); } - @Override - public String toString() { return dsfMIArg.toString(); } - } - // Check if the stopped event can be used to extract the variable value. if (execDmc != null && miVariableDmc.fType == MIVariableDMC.Type.ARGUMENT && frameDmc.fLevel == 0 && fCachedStoppedEvent != null && fCachedStoppedEvent.getFrame() != null && @@ -694,8 +717,7 @@ public class MIStack extends AbstractDsfService }); } }); - }//if - if (miVariableDmc.fType == MIVariableDMC.Type.LOCAL){ + } else if (miVariableDmc.fType == MIVariableDMC.Type.LOCAL){ fMICommandCache.execute( // Don't ask for value when we are visualizing trace data, since some // data will not be there, and the command will fail @@ -735,7 +757,17 @@ public class MIStack extends AbstractDsfService }); } }); - }//if + } else if (miVariableDmc.fType == MIVariableDMC.Type.RETURN_VALUES) { + VariableData var = fThreadToReturnVariable.get(execDmc); + if (var != null) { + rm.setData(var); + } else { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, "Return value not found", null)); //$NON-NLS-1$ + } + rm.done(); + } else { + rm.done(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, "Invalid variable type " + miVariableDmc.fType, null)); //$NON-NLS-1$ + } } @@ -767,7 +799,24 @@ public class MIStack extends AbstractDsfService return -1; } - + /** + * Retrieves variables which are used to store the return values of functions. + */ + private void getReturnValues(IFrameDMContext frameDmc, DataRequestMonitor rm) { + IVariableDMContext[] values = new IVariableDMContext[0]; + + // Return values are only relevant for the top stack-frame + if (!fTraceVisualization && frameDmc.getLevel() == 0) { + IMIExecutionDMContext threadDmc = DMContexts.getAncestorOfType(frameDmc, IMIExecutionDMContext.class); + VariableData var = fThreadToReturnVariable.get(threadDmc); + if (var != null) { + values = new IVariableDMContext[1]; + values[0] = new MIVariableDMC(this, frameDmc, MIVariableDMC.Type.RETURN_VALUES, 0); + } + } + rm.done(values); + } + @Override public void getLocals(final IFrameDMContext frameDmc, final DataRequestMonitor rm) { @@ -780,8 +829,20 @@ public class MIStack extends AbstractDsfService rm.done(); } }; - countingRm.setDoneCount(2); + 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(); + } + }); + + // Then show arguments getArguments( frameDmc, new DataRequestMonitor(getExecutor(), countingRm) { @@ -792,6 +853,7 @@ public class MIStack extends AbstractDsfService } }); + // 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 @@ -914,6 +976,29 @@ public class MIStack extends AbstractDsfService fMICommandCache.reset(); fStackDepthCache.clear(); } + + handleReturnValues(e); + } + + private void handleReturnValues(IResumedDMEvent e) { + // Whenever the execution resumes, we can clear any + // return values of previous methods for the resuming + // thread context. For all-stop mode, we get a container event here, + // and we can clear the entire list, which should contain at most one + // value for all-stop. + if (e instanceof IContainerResumedDMEvent) { + // All-stop mode + assert fThreadToReturnVariable.size() <= 1; + fThreadToReturnVariable.clear(); + } else { + // Non-stop mode + IDMContext ctx = e.getDMContext(); + if (ctx instanceof IMIExecutionDMContext) { + fThreadToReturnVariable.remove(ctx); + } else if (ctx instanceof IContainerDMContext) { + fThreadToReturnVariable.clear(); + } + } } /** @@ -926,8 +1011,47 @@ public class MIStack extends AbstractDsfService fMICommandCache.setContextAvailable(e.getDMContext(), true); fMICommandCache.reset(); fStackDepthCache.clear(); + + handleReturnValues(e); } + private void handleReturnValues(ISuspendedDMEvent e) { + // Process MIFunctionFinishedEvent from within the ISuspendedDMEvent + // instead of MIStoppedEvent. + // This avoids a race conditions where the actual MIFunctionFinishedEvent + // can arrive here, faster that a preceding IResumedDMEvent + if (e instanceof IMIDMEvent) { + Object miEvent = ((IMIDMEvent)e).getMIEvent(); + if (miEvent instanceof MIFunctionFinishedEvent) { + // When returning out of a function, we want to show the return value + // for the thread that finished the call. To do that, we store + // the variable in which GDB stores that return value, and we do + // that for the proper thread. + + IMIExecutionDMContext finishedEventThread = null; + if (e instanceof IContainerSuspendedDMEvent) { + // All-stop mode + IExecutionDMContext[] triggerContexts = ((IContainerSuspendedDMEvent)e).getTriggeringContexts(); + if (triggerContexts.length != 0 && triggerContexts[0] instanceof IMIExecutionDMContext) { + finishedEventThread = (IMIExecutionDMContext)triggerContexts[0]; + } + } else { + // Non-stop mode + IDMContext ctx = e.getDMContext(); + if (ctx instanceof IMIExecutionDMContext) { + finishedEventThread = (IMIExecutionDMContext)ctx; + } + } + + if (finishedEventThread != null) { + String name = ((MIFunctionFinishedEvent)miEvent).getGDBResultVar(); + String value = ((MIFunctionFinishedEvent)miEvent).getReturnValue(); + + fThreadToReturnVariable.put(finishedEventThread, new VariableData(new MIArg(name, value))); + } + } + } + } /** * @nooverride This method is not intended to be re-implemented or extended by clients. diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/Messages.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/Messages.java index 50b394e7f42..68a8b1f871e 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/Messages.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/Messages.java @@ -21,6 +21,7 @@ class Messages extends NLS { public static String Breakpoint_attribute_problem; public static String Breakpoint_installation_failed; public static String MIExpressions_NotAvailableBecauseChildOfDynamicVarobj; + public static String MIExpressions_ReturnValueAlias; static { // initialize resource bundle diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/Messages.properties b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/Messages.properties index 8114e6d0e19..526b7f35780 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/Messages.properties +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/Messages.properties @@ -14,3 +14,4 @@ Breakpoint_attribute_problem=Breakpoint attribute problem: {0} Breakpoint_installation_failed=installation failed MIExpressions_NotAvailableBecauseChildOfDynamicVarobj=N/A (child of pretty-printed object) +MIExpressions_ReturnValueAlias={0} returned diff --git a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/data/launch/src/ExpressionTestApp.cc b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/data/launch/src/ExpressionTestApp.cc index 66f2c4f3a6b..c63193c5e51 100644 --- a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/data/launch/src/ExpressionTestApp.cc +++ b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/data/launch/src/ExpressionTestApp.cc @@ -21,14 +21,24 @@ char *gCharPtr2 = (char*)0x4321; bool *gBoolPtr2 = (bool*)0x12ABCDEF; class bar { -public: +public: + bar() { + d = 8; + e[0] = 18; + e[1] = 28; + } int d; private: int e[2]; }; class bar2 { -public: +public: + bar2() { + f = 318; + g[0] = 228; + g[1] = 138; + } int f; private: int g[2]; @@ -36,6 +46,11 @@ private: class foo: public bar, bar2 { public: + foo() { + c = 8; + a[0] = 1000; + a[1] = 23; + } int a[2]; bar b; private: @@ -350,6 +365,28 @@ int testRTTI() { } // End of bug 376901 RTTI tests +int testSimpleReturn(int a) { + int b = 0; + b = a; + return b; +} + +foo testComplexReturn() { + foo f; + int a = 8; + + return f; +} + +void testReturn() { + int a = 10; + bool b = false; + + testSimpleReturn(6); + testComplexReturn(); + a = 0;; +} + int main() { printf("Running ExpressionTest App\n"); @@ -377,10 +414,12 @@ int main() { testArrays(); testRTTI(); testCasting(); + testReturn(); // For bug 320277 BaseTest b; b.test(); + return 0; } diff --git a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/framework/SyncUtil.java b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/framework/SyncUtil.java index 3e66f55b983..e78d4156405 100644 --- a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/framework/SyncUtil.java +++ b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/framework/SyncUtil.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2007, 2010 Ericsson and others. + * Copyright (c) 2007, 2013 Ericsson 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 @@ -12,15 +12,21 @@ package org.eclipse.cdt.tests.dsf.gdb.framework; import static org.junit.Assert.fail; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.concurrent.Callable; import java.util.concurrent.TimeUnit; -import junit.framework.Assert; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertEquals; +import org.eclipse.cdt.dsf.concurrent.CountingRequestMonitor; import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; import org.eclipse.cdt.dsf.concurrent.ImmediateDataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.ImmediateExecutor; import org.eclipse.cdt.dsf.concurrent.Query; import org.eclipse.cdt.dsf.concurrent.ThreadSafeAndProhibitedFromDsfExecutor; import org.eclipse.cdt.dsf.datamodel.DMContexts; @@ -39,6 +45,8 @@ import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext; import org.eclipse.cdt.dsf.debug.service.IRunControl.StepType; import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMContext; import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMData; +import org.eclipse.cdt.dsf.debug.service.IStack.IVariableDMContext; +import org.eclipse.cdt.dsf.debug.service.IStack.IVariableDMData; import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin; import org.eclipse.cdt.dsf.gdb.launching.GdbLaunch; import org.eclipse.cdt.dsf.gdb.service.IGDBProcesses; @@ -607,10 +615,10 @@ public class SyncUtil { protected void handleCompleted() { if (isSuccess()) { IDMContext[] contexts = getData(); - Assert.assertNotNull("invalid return value from service", contexts); - Assert.assertEquals("unexpected number of processes", 1, contexts.length); + assertNotNull("invalid return value from service", contexts); + assertEquals("unexpected number of processes", 1, contexts.length); IDMContext context = contexts[0]; - Assert.assertNotNull("unexpected process context type ", context); + assertNotNull("unexpected process context type ", context); rm.done((IContainerDMContext)context); } else { rm.done(getStatus()); @@ -648,7 +656,7 @@ public class SyncUtil { protected void handleCompleted() { if (isSuccess()) { IDMContext[] threads = getData(); - Assert.assertNotNull("invalid return value from service", threads); + assertNotNull("invalid return value from service", threads); rm.setData((IMIExecutionDMContext[])threads); } else { rm.setStatus(getStatus()); @@ -673,8 +681,8 @@ public class SyncUtil { @ThreadSafeAndProhibitedFromDsfExecutor("fSession.getExecutor()") public static IMIExecutionDMContext getExecutionContext(int threadIndex) throws InterruptedException { IMIExecutionDMContext[] threads = getExecutionContexts(); - Assert.assertTrue("unexpected number of threads", threadIndex < threads.length); - Assert.assertNotNull("unexpected thread context type ", threads[threadIndex]); + assertTrue("unexpected number of threads", threadIndex < threads.length); + assertNotNull("unexpected thread context type ", threads[threadIndex]); return threads[threadIndex]; } @@ -748,4 +756,45 @@ public class SyncUtil { } return event; } + + public static IVariableDMData[] getLocals(final IFrameDMContext frameDmc) throws Throwable { + Query query = new Query() { + @Override + protected void execute(final DataRequestMonitor rm) { + fStack.getLocals(frameDmc, new ImmediateDataRequestMonitor() { + @Override + protected void handleCompleted() { + if (isSuccess()) { + IVariableDMContext[] varDmcs = getData(); + final List localsDMData = new ArrayList(); + final CountingRequestMonitor crm = new CountingRequestMonitor(ImmediateExecutor.getInstance(), rm) { + @Override + protected void handleSuccess() { + rm.done(localsDMData.toArray(new IVariableDMData[localsDMData.size()])); + }; + }; + + for (IVariableDMContext varDmc : varDmcs) { + fStack.getVariableData(varDmc, + new ImmediateDataRequestMonitor(crm) { + @Override + public void handleSuccess() { + localsDMData.add(getData()); + crm.done(); + } + }); + } + crm.setDoneCount(varDmcs.length); + } else { + rm.done(); + } + } + }); + } + }; + + fSession.getExecutor().execute(query); + IVariableDMData[] result = query.get(500, TimeUnit.MILLISECONDS); + return result; + } } diff --git a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/MIExpressionsTest.java b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/MIExpressionsTest.java index f1f7083e23d..5c1ad39d111 100644 --- a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/MIExpressionsTest.java +++ b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/MIExpressionsTest.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2007, 2010 Ericsson and others. + * Copyright (c) 2007, 2013 Ericsson 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 @@ -40,6 +40,7 @@ import org.eclipse.cdt.dsf.debug.service.IFormattedValues.FormattedValueDMContex import org.eclipse.cdt.dsf.debug.service.IFormattedValues.FormattedValueDMData; import org.eclipse.cdt.dsf.debug.service.IRunControl.StepType; import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMContext; +import org.eclipse.cdt.dsf.debug.service.IStack.IVariableDMData; import org.eclipse.cdt.dsf.mi.service.ClassAccessor.MIExpressionDMCAccessor; import org.eclipse.cdt.dsf.mi.service.MIExpressions; import org.eclipse.cdt.dsf.mi.service.command.events.MIStoppedEvent; @@ -310,11 +311,13 @@ public class MIExpressionsTest extends BaseTestCase { // Get the children of some variables MIStoppedEvent stoppedEvent = SyncUtil.runToLocation("testChildren"); - doTestChildren(stoppedEvent); + IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); + IExpressionDMContext exprDMC = SyncUtil.createExpression(frameDmc, "f"); + doTestChildren(exprDMC); // Now do a step and get the children again, to test the internal cache - stoppedEvent = SyncUtil.step(1, StepType.STEP_OVER); - doTestChildren(stoppedEvent); + SyncUtil.step(1, StepType.STEP_OVER); + doTestChildren(exprDMC); } /** @@ -3188,12 +3191,8 @@ public class MIExpressionsTest extends BaseTestCase { } } - private void doTestChildren(MIStoppedEvent stoppedEvent) throws Throwable { - - final IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); - - final IExpressionDMContext exprDMC = SyncUtil.createExpression(frameDmc, "f"); - + private void doTestChildren(IExpressionDMContext exprDMC) throws Throwable + { IExpressionDMContext[] children = getChildren(exprDMC, new String[] {"bar", "bar2", "a", "b", "c"}); @@ -3598,20 +3597,15 @@ public class MIExpressionsTest extends BaseTestCase { Query query = new Query() { @Override protected void execute(final DataRequestMonitor rm) { - fExpService.getExecutor().submit(new Runnable() { - @Override - public void run() { - fExpService.getFormattedExpressionValue( - fExpService.getFormattedValueContext(children[0], IFormattedValues.NATURAL_FORMAT), - new ImmediateDataRequestMonitor(rm) { - @Override - protected void handleCompleted() { - rm.done(getData().getFormattedValue()); - } - }); - } - }); - } + fExpService.getFormattedExpressionValue( + fExpService.getFormattedValueContext(children[0], IFormattedValues.NATURAL_FORMAT), + new ImmediateDataRequestMonitor(rm) { + @Override + protected void handleCompleted() { + rm.done(getData().getFormattedValue()); + } + }); + } }; fSession.getExecutor().execute(query); @@ -3622,20 +3616,15 @@ public class MIExpressionsTest extends BaseTestCase { query = new Query() { @Override protected void execute(final DataRequestMonitor rm) { - fExpService.getExecutor().submit(new Runnable() { - @Override - public void run() { - fExpService.getFormattedExpressionValue( - fExpService.getFormattedValueContext(castChildren[0], IFormattedValues.NATURAL_FORMAT), - new ImmediateDataRequestMonitor(rm) { - @Override - protected void handleCompleted() { - rm.done(getData().getFormattedValue()); - } - }); - } - }); - } + fExpService.getFormattedExpressionValue( + fExpService.getFormattedValueContext(castChildren[0], IFormattedValues.NATURAL_FORMAT), + new ImmediateDataRequestMonitor(rm) { + @Override + protected void handleCompleted() { + rm.done(getData().getFormattedValue()); + } + }); + } }; fSession.getExecutor().execute(query); value = query.get(500, TimeUnit.MILLISECONDS); @@ -3675,20 +3664,15 @@ public class MIExpressionsTest extends BaseTestCase { Query query = new Query() { @Override protected void execute(final DataRequestMonitor rm) { - fExpService.getExecutor().submit(new Runnable() { - @Override - public void run() { - fExpService.getFormattedExpressionValue( - fExpService.getFormattedValueContext(child, IFormattedValues.NATURAL_FORMAT), - new ImmediateDataRequestMonitor(rm) { - @Override - protected void handleCompleted() { - rm.done(getData().getFormattedValue()); - } - }); - } - }); - } + fExpService.getFormattedExpressionValue( + fExpService.getFormattedValueContext(child, IFormattedValues.NATURAL_FORMAT), + new ImmediateDataRequestMonitor(rm) { + @Override + protected void handleCompleted() { + rm.done(getData().getFormattedValue()); + } + }); + } }; fSession.getExecutor().execute(query); @@ -3738,20 +3722,15 @@ public class MIExpressionsTest extends BaseTestCase { Query query = new Query() { @Override protected void execute(final DataRequestMonitor rm) { - fExpService.getExecutor().submit(new Runnable() { - @Override - public void run() { - fExpService.getFormattedExpressionValue( - fExpService.getFormattedValueContext(child, IFormattedValues.NATURAL_FORMAT), - new ImmediateDataRequestMonitor(rm) { - @Override - protected void handleCompleted() { - rm.done(getData().getFormattedValue()); - } - }); - } - }); - } + fExpService.getFormattedExpressionValue( + fExpService.getFormattedValueContext(child, IFormattedValues.NATURAL_FORMAT), + new ImmediateDataRequestMonitor(rm) { + @Override + protected void handleCompleted() { + rm.done(getData().getFormattedValue()); + } + }); + } }; fSession.getExecutor().execute(query); @@ -3801,20 +3780,15 @@ public class MIExpressionsTest extends BaseTestCase { Query query = new Query() { @Override protected void execute(final DataRequestMonitor rm) { - fExpService.getExecutor().submit(new Runnable() { - @Override - public void run() { - fExpService.getFormattedExpressionValue( - fExpService.getFormattedValueContext(child, IFormattedValues.NATURAL_FORMAT), - new ImmediateDataRequestMonitor(rm) { - @Override - protected void handleCompleted() { - rm.done(getData().getFormattedValue()); - } - }); - } - }); - } + fExpService.getFormattedExpressionValue( + fExpService.getFormattedValueContext(child, IFormattedValues.NATURAL_FORMAT), + new ImmediateDataRequestMonitor(rm) { + @Override + protected void handleCompleted() { + rm.done(getData().getFormattedValue()); + } + }); + } }; fSession.getExecutor().execute(query); @@ -3871,20 +3845,15 @@ public class MIExpressionsTest extends BaseTestCase { Query query = new Query() { @Override protected void execute(final DataRequestMonitor rm) { - fExpService.getExecutor().submit(new Runnable() { - @Override - public void run() { - fExpService.getFormattedExpressionValue( - fExpService.getFormattedValueContext(child, IFormattedValues.NATURAL_FORMAT), - new ImmediateDataRequestMonitor(rm) { - @Override - protected void handleCompleted() { - rm.done(getData().getFormattedValue()); - } - }); - } - }); - } + fExpService.getFormattedExpressionValue( + fExpService.getFormattedValueContext(child, IFormattedValues.NATURAL_FORMAT), + new ImmediateDataRequestMonitor(rm) { + @Override + protected void handleCompleted() { + rm.done(getData().getFormattedValue()); + } + }); + } }; fSession.getExecutor().execute(query); @@ -3947,20 +3916,15 @@ public class MIExpressionsTest extends BaseTestCase { Query query = new Query() { @Override protected void execute(final DataRequestMonitor rm) { - fExpService.getExecutor().submit(new Runnable() { - @Override - public void run() { - fExpService.getFormattedExpressionValue( - fExpService.getFormattedValueContext(child, IFormattedValues.NATURAL_FORMAT), - new ImmediateDataRequestMonitor(rm) { - @Override - protected void handleCompleted() { - rm.done(getData().getFormattedValue()); - } - }); - } - }); - } + fExpService.getFormattedExpressionValue( + fExpService.getFormattedValueContext(child, IFormattedValues.NATURAL_FORMAT), + new ImmediateDataRequestMonitor(rm) { + @Override + protected void handleCompleted() { + rm.done(getData().getFormattedValue()); + } + }); + } }; fSession.getExecutor().execute(query); @@ -4021,20 +3985,15 @@ public class MIExpressionsTest extends BaseTestCase { Query query = new Query() { @Override protected void execute(final DataRequestMonitor rm) { - fExpService.getExecutor().submit(new Runnable() { - @Override - public void run() { - fExpService.getFormattedExpressionValue( - fExpService.getFormattedValueContext(child, IFormattedValues.NATURAL_FORMAT), - new ImmediateDataRequestMonitor(rm) { - @Override - protected void handleCompleted() { - rm.done(getData().getFormattedValue()); - } - }); - } - }); - } + fExpService.getFormattedExpressionValue( + fExpService.getFormattedValueContext(child, IFormattedValues.NATURAL_FORMAT), + new ImmediateDataRequestMonitor(rm) { + @Override + protected void handleCompleted() { + rm.done(getData().getFormattedValue()); + } + }); + } }; fSession.getExecutor().execute(query); @@ -4046,6 +4005,105 @@ public class MIExpressionsTest extends BaseTestCase { assertEquals(castExprDmc.getParents()[0], exprDmc); } + /** + * This test verifies that we display the simple return value of a method after + * a step-return operation, but only for the first stack frame. + */ + @Test + public void testDisplaySimpleReturnValueForStepReturn() throws Throwable { + SyncUtil.runToLocation("testSimpleReturn"); + MIStoppedEvent stoppedEvent = SyncUtil.step(1, StepType.STEP_RETURN); + + // Check the return value is shown when looking at the first frame + final IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); + IVariableDMData[] result = SyncUtil.getLocals(frameDmc); + + assertEquals(3, result.length); // Two variables and one return value + + // Return value + assertEquals("$1", result[0].getName()); + assertEquals("6", result[0].getValue()); + // first variable + assertEquals("a", result[1].getName()); + assertEquals("10", result[1].getValue()); + // Second variable + assertEquals("b", result[2].getName()); + assertEquals("false", result[2].getValue()); + + // Now check how the return value will be displayed to the user + final IExpressionDMContext returnExprDmc = SyncUtil.createExpression(frameDmc, "$1"); + Query query = new Query() { + @Override + protected void execute(final DataRequestMonitor rm) { + fExpService.getExpressionData(returnExprDmc, rm); + } + }; + fSession.getExecutor().execute(query); + IExpressionDMData data = query.get(500, TimeUnit.MILLISECONDS); + assertEquals("testSimpleReturn() returned", data.getName()); + + // Now check the actual value using the expression service + String value = SyncUtil.getExpressionValue(returnExprDmc, IFormattedValues.DECIMAL_FORMAT); + assertEquals("6", value); + + // Now make sure we don't show the return value for another frame + final IFrameDMContext frameDmc2 = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 1); + result = SyncUtil.getLocals(frameDmc2); + + // only one variable + assertEquals(1, result.length); + assertEquals("b", result[0].getName()); + } + + /** + * This test verifies that we display the complex return value of a method after + * a step-return operation, but only for the first stack frame. + */ + @Test + public void testDisplayComplexReturnValueForStepReturn() throws Throwable { + SyncUtil.runToLocation("testComplexReturn"); + MIStoppedEvent stoppedEvent = SyncUtil.step(1, StepType.STEP_RETURN); + + // Check the return value is show when looking at the first frame + final IFrameDMContext frameDmc = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 0); + IVariableDMData[] result = SyncUtil.getLocals(frameDmc); + + assertEquals(3, result.length); // Two variables and one return value + + // Return value + assertEquals("$1", result[0].getName()); + + // first variable + assertEquals("a", result[1].getName()); + assertEquals("10", result[1].getValue()); + // Second variable + assertEquals("b", result[2].getName()); + assertEquals("false", result[2].getValue()); + + // Now check how the return value will be displayed to the user + final IExpressionDMContext returnExprDmc = SyncUtil.createExpression(frameDmc, "$1"); + Query query = new Query() { + @Override + protected void execute(final DataRequestMonitor rm) { + fExpService.getExpressionData(returnExprDmc, rm); + } + }; + fSession.getExecutor().execute(query); + IExpressionDMData data = query.get(500, TimeUnit.MILLISECONDS); + assertEquals("testComplexReturn() returned", data.getName()); + + // Now check the content of the complex return expression + doTestChildren(returnExprDmc); + + // Now make sure we don't show the return value for another frame + IFrameDMContext frameDmc2 = SyncUtil.getStackFrame(stoppedEvent.getDMContext(), 1); + result = SyncUtil.getLocals(frameDmc2); + + // only one variable + assertEquals(1, result.length); + assertEquals("b", result[0].getName()); + } + protected int getChildrenCount(final IExpressionDMContext parentDmc, final int expectedCount) throws Throwable { final AsyncCompletionWaitor wait = new AsyncCompletionWaitor(); @@ -4098,20 +4156,15 @@ public class MIExpressionsTest extends BaseTestCase { Query query = new Query() { @Override protected void execute(final DataRequestMonitor rm) { - fExpService.getExecutor().submit(new Runnable() { - @Override - public void run() { - fExpService.getExpressionData( - exprDmc, - new ImmediateDataRequestMonitor(rm) { - @Override - protected void handleCompleted() { - rm.done(getData().getTypeName()); - } - }); - } - }); - } + fExpService.getExpressionData( + exprDmc, + new ImmediateDataRequestMonitor(rm) { + @Override + protected void handleCompleted() { + rm.done(getData().getTypeName()); + } + }); + } }; fSession.getExecutor().execute(query); diff --git a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/tests_7_0/MIExpressionsNonStopTest_7_0.java b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/tests_7_0/MIExpressionsNonStopTest_7_0.java new file mode 100644 index 00000000000..c2100c74a38 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/tests_7_0/MIExpressionsNonStopTest_7_0.java @@ -0,0 +1,31 @@ +/******************************************************************************* + * Copyright (c) 2013 Ericsson 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: + * Marc Khouzam (Ericsson) - Initial Implementation + *******************************************************************************/ +package org.eclipse.cdt.tests.dsf.gdb.tests.tests_7_0; + +import org.eclipse.cdt.dsf.gdb.IGDBLaunchConfigurationConstants; +import org.eclipse.cdt.tests.dsf.gdb.framework.BackgroundRunner; +import org.eclipse.cdt.tests.dsf.gdb.tests.ITestConstants; +import org.junit.runner.RunWith; + +@RunWith(BackgroundRunner.class) +public class MIExpressionsNonStopTest_7_0 extends MIExpressionsTest_7_0 { + @Override + protected void setGdbVersion() { + setGdbProgramNamesLaunchAttributes(ITestConstants.SUFFIX_GDB_7_0); + } + + @Override + protected void setLaunchAttributes() { + super.setLaunchAttributes(); + + setLaunchAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_NON_STOP, true); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/tests_7_1/MIExpressionsNonStopTest_7_1.java b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/tests_7_1/MIExpressionsNonStopTest_7_1.java new file mode 100644 index 00000000000..da20a623f50 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/tests_7_1/MIExpressionsNonStopTest_7_1.java @@ -0,0 +1,31 @@ +/******************************************************************************* + * Copyright (c) 2013 Ericsson 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: + * Marc Khouzam (Ericsson) - Initial Implementation + *******************************************************************************/ +package org.eclipse.cdt.tests.dsf.gdb.tests.tests_7_1; + +import org.eclipse.cdt.dsf.gdb.IGDBLaunchConfigurationConstants; +import org.eclipse.cdt.tests.dsf.gdb.framework.BackgroundRunner; +import org.eclipse.cdt.tests.dsf.gdb.tests.ITestConstants; +import org.junit.runner.RunWith; + +@RunWith(BackgroundRunner.class) +public class MIExpressionsNonStopTest_7_1 extends MIExpressionsTest_7_1 { + @Override + protected void setGdbVersion() { + setGdbProgramNamesLaunchAttributes(ITestConstants.SUFFIX_GDB_7_1); + } + + @Override + protected void setLaunchAttributes() { + super.setLaunchAttributes(); + + setLaunchAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_NON_STOP, true); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/tests_7_2/MIExpressionsNonStopTest_7_2.java b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/tests_7_2/MIExpressionsNonStopTest_7_2.java new file mode 100644 index 00000000000..6fcf6c6e7da --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/tests_7_2/MIExpressionsNonStopTest_7_2.java @@ -0,0 +1,31 @@ +/******************************************************************************* + * Copyright (c) 2013 Ericsson 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: + * Marc Khouzam (Ericsson) - Initial Implementation + *******************************************************************************/ +package org.eclipse.cdt.tests.dsf.gdb.tests.tests_7_2; + +import org.eclipse.cdt.dsf.gdb.IGDBLaunchConfigurationConstants; +import org.eclipse.cdt.tests.dsf.gdb.framework.BackgroundRunner; +import org.eclipse.cdt.tests.dsf.gdb.tests.ITestConstants; +import org.junit.runner.RunWith; + +@RunWith(BackgroundRunner.class) +public class MIExpressionsNonStopTest_7_2 extends MIExpressionsTest_7_2 { + @Override + protected void setGdbVersion() { + setGdbProgramNamesLaunchAttributes(ITestConstants.SUFFIX_GDB_7_2); + } + + @Override + protected void setLaunchAttributes() { + super.setLaunchAttributes(); + + setLaunchAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_NON_STOP, true); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/tests_7_3/MIExpressionsNonStopTest_7_3.java b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/tests_7_3/MIExpressionsNonStopTest_7_3.java new file mode 100644 index 00000000000..522ff32bdd9 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/tests_7_3/MIExpressionsNonStopTest_7_3.java @@ -0,0 +1,31 @@ +/******************************************************************************* + * Copyright (c) 2013 Ericsson 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: + * Marc Khouzam (Ericsson) - Initial Implementation + *******************************************************************************/ +package org.eclipse.cdt.tests.dsf.gdb.tests.tests_7_3; + +import org.eclipse.cdt.dsf.gdb.IGDBLaunchConfigurationConstants; +import org.eclipse.cdt.tests.dsf.gdb.framework.BackgroundRunner; +import org.eclipse.cdt.tests.dsf.gdb.tests.ITestConstants; +import org.junit.runner.RunWith; + +@RunWith(BackgroundRunner.class) +public class MIExpressionsNonStopTest_7_3 extends MIExpressionsTest_7_3 { + @Override + protected void setGdbVersion() { + setGdbProgramNamesLaunchAttributes(ITestConstants.SUFFIX_GDB_7_3); + } + + @Override + protected void setLaunchAttributes() { + super.setLaunchAttributes(); + + setLaunchAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_NON_STOP, true); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/tests_7_4/MIExpressionsNonStopTest_7_4.java b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/tests_7_4/MIExpressionsNonStopTest_7_4.java new file mode 100644 index 00000000000..9e488c692e8 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/tests_7_4/MIExpressionsNonStopTest_7_4.java @@ -0,0 +1,31 @@ +/******************************************************************************* + * Copyright (c) 2013 Ericsson 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: + * Marc Khouzam (Ericsson) - Initial Implementation + *******************************************************************************/ +package org.eclipse.cdt.tests.dsf.gdb.tests.tests_7_4; + +import org.eclipse.cdt.dsf.gdb.IGDBLaunchConfigurationConstants; +import org.eclipse.cdt.tests.dsf.gdb.framework.BackgroundRunner; +import org.eclipse.cdt.tests.dsf.gdb.tests.ITestConstants; +import org.junit.runner.RunWith; + +@RunWith(BackgroundRunner.class) +public class MIExpressionsNonStopTest_7_4 extends MIExpressionsTest_7_4 { + @Override + protected void setGdbVersion() { + setGdbProgramNamesLaunchAttributes(ITestConstants.SUFFIX_GDB_7_4); + } + + @Override + protected void setLaunchAttributes() { + super.setLaunchAttributes(); + + setLaunchAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_NON_STOP, true); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/tests_7_5/MIExpressionsNonStopTest_7_5.java b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/tests_7_5/MIExpressionsNonStopTest_7_5.java new file mode 100644 index 00000000000..8d7fa221c03 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/tests_7_5/MIExpressionsNonStopTest_7_5.java @@ -0,0 +1,31 @@ +/******************************************************************************* + * Copyright (c) 2013 Ericsson 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: + * Marc Khouzam (Ericsson) - Initial Implementation + *******************************************************************************/ +package org.eclipse.cdt.tests.dsf.gdb.tests.tests_7_5; + +import org.eclipse.cdt.dsf.gdb.IGDBLaunchConfigurationConstants; +import org.eclipse.cdt.tests.dsf.gdb.framework.BackgroundRunner; +import org.eclipse.cdt.tests.dsf.gdb.tests.ITestConstants; +import org.junit.runner.RunWith; + +@RunWith(BackgroundRunner.class) +public class MIExpressionsNonStopTest_7_5 extends MIExpressionsTest_7_5 { + @Override + protected void setGdbVersion() { + setGdbProgramNamesLaunchAttributes(ITestConstants.SUFFIX_GDB_7_5); + } + + @Override + protected void setLaunchAttributes() { + super.setLaunchAttributes(); + + setLaunchAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_NON_STOP, true); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/tests_7_6/MIExpressionsNonStopTest_7_6.java b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/tests_7_6/MIExpressionsNonStopTest_7_6.java new file mode 100644 index 00000000000..6773eb55889 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/tests_7_6/MIExpressionsNonStopTest_7_6.java @@ -0,0 +1,31 @@ +/******************************************************************************* + * Copyright (c) 2013 Ericsson 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: + * Marc Khouzam (Ericsson) - Initial Implementation + *******************************************************************************/ +package org.eclipse.cdt.tests.dsf.gdb.tests.tests_7_6; + +import org.eclipse.cdt.dsf.gdb.IGDBLaunchConfigurationConstants; +import org.eclipse.cdt.tests.dsf.gdb.framework.BackgroundRunner; +import org.eclipse.cdt.tests.dsf.gdb.tests.ITestConstants; +import org.junit.runner.RunWith; + +@RunWith(BackgroundRunner.class) +public class MIExpressionsNonStopTest_7_6 extends MIExpressionsTest_7_6 { + @Override + protected void setGdbVersion() { + setGdbProgramNamesLaunchAttributes(ITestConstants.SUFFIX_GDB_7_6); + } + + @Override + protected void setLaunchAttributes() { + super.setLaunchAttributes(); + + setLaunchAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_NON_STOP, true); + } +}