diff options
37 files changed, 3023 insertions, 117 deletions
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditorMessages.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditorMessages.java index 5d0e53a8d03..c17f8e93625 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditorMessages.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditorMessages.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2010 QNX Software Systems and others. + * Copyright (c) 2000, 2013 QNX Software Systems and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -8,6 +8,7 @@ * Contributors: * QNX Software Systems - Initial API and implementation * Tomasz Wesolowski + * Alvaro Sanchez-Leon (Ericsson AB) *******************************************************************************/ package org.eclipse.cdt.internal.ui.editor; @@ -106,6 +107,7 @@ public final class CEditorMessages extends NLS { public static String CEditor_markOccurrences_job_name; public static String CEditor_index_expander_job_name; public static String CEditorActionContributor_ExpandSelectionMenu_label; + public static String StepIntoSelection_unable_to_resolve_name; static { NLS.initializeMessages(CEditorMessages.class.getName(), CEditorMessages.class); diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditorMessages.properties b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditorMessages.properties index 62aa9c4416e..eeb64c72004 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditorMessages.properties +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditorMessages.properties @@ -1,5 +1,5 @@ ######################################### -# Copyright (c) 2005, 2012 IBM Corporation and others. +# Copyright (c) 2005, 2013 IBM Corporation 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 @@ -13,6 +13,7 @@ # Sergey Prigogin (Google) # Tomasz Wesolowski # Mathias Kunter +# Alvaro Sanchez-Leon (Ericsson) ######################################### AddIncludeOnSelection_label=Add Include @@ -105,3 +106,4 @@ SemanticHighlighting_externalSDK= External SDK calls CEditor_markOccurrences_job_name= Occurrences Marker CEditor_index_expander_job_name= Index Expander CEditorActionContributor_ExpandSelectionMenu_label=E&xpand Selection To +StepIntoSelection_unable_to_resolve_name=Unable to resolve the selection to a semantic object
\ No newline at end of file diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/SelectionToDeclarationJob.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/SelectionToDeclarationJob.java new file mode 100644 index 00000000000..fc4abb4b058 --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/SelectionToDeclarationJob.java @@ -0,0 +1,431 @@ +/******************************************************************************* + * Copyright (c) 2013 Ericsson AB 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: + * Alvaro Sanchez-Leon (Ericsson AB) - Support for Step into selection (bug 244865) + *******************************************************************************/ +package org.eclipse.cdt.internal.ui.editor; + +import static java.lang.Math.max; +import static java.lang.Math.min; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.jface.text.IRegion; +import org.eclipse.jface.text.ITextSelection; +import org.eclipse.jface.text.Region; +import org.eclipse.ui.texteditor.ITextEditor; + +import org.eclipse.cdt.core.CCorePlugin; +import org.eclipse.cdt.core.dom.IName; +import org.eclipse.cdt.core.dom.ast.IASTDeclaration; +import org.eclipse.cdt.core.dom.ast.IASTFileLocation; +import org.eclipse.cdt.core.dom.ast.IASTFunctionDefinition; +import org.eclipse.cdt.core.dom.ast.IASTImplicitName; +import org.eclipse.cdt.core.dom.ast.IASTImplicitNameOwner; +import org.eclipse.cdt.core.dom.ast.IASTName; +import org.eclipse.cdt.core.dom.ast.IASTNode; +import org.eclipse.cdt.core.dom.ast.IASTNodeSelector; +import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit; +import org.eclipse.cdt.core.dom.ast.IBinding; +import org.eclipse.cdt.core.dom.ast.IParameter; +import org.eclipse.cdt.core.dom.ast.IProblemBinding; +import org.eclipse.cdt.core.dom.ast.IType; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTemplateDeclaration; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTUsingDeclaration; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPMethod; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPSpecialization; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPTemplateParameter; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPUsingDeclaration; +import org.eclipse.cdt.core.index.IIndex; +import org.eclipse.cdt.core.index.IIndexManager; +import org.eclipse.cdt.core.index.IIndexName; +import org.eclipse.cdt.core.model.ICElement; +import org.eclipse.cdt.core.model.ICProject; +import org.eclipse.cdt.core.model.IFunctionDeclaration; +import org.eclipse.cdt.core.model.ILanguage; +import org.eclipse.cdt.core.model.ITranslationUnit; +import org.eclipse.cdt.core.parser.util.ArrayUtil; +import org.eclipse.cdt.ui.CUIPlugin; + +import org.eclipse.cdt.internal.core.dom.parser.ASTNode; +import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPVisitor; +import org.eclipse.cdt.internal.core.index.IIndexFragmentName; +import org.eclipse.cdt.internal.core.model.ASTCache.ASTRunnable; +import org.eclipse.cdt.internal.core.model.ext.CElementHandleFactory; +import org.eclipse.cdt.internal.core.model.ext.ICElementHandle; + +import org.eclipse.cdt.internal.ui.ICStatusConstants; +import org.eclipse.cdt.internal.ui.viewsupport.IndexUI; + +/** + * Based on class OpenDeclarationsJob + * @author Alvaro Sanchez-Leon + * @since 5.6 + */ +public class SelectionToDeclarationJob extends Job implements ASTRunnable { + private enum NameKind { + REFERENCE, DECLARATION, USING_DECL, DEFINITION + } + + private final ITranslationUnit fTranslationUnit; + private IIndex fIndex; + private final ITextSelection fTextSelection; + private IFunctionDeclaration[] fResolvedFunctions; + + public SelectionToDeclarationJob(ITextEditor editor, ITextSelection textSelection) throws CoreException { + super(CEditorMessages.OpenDeclarations_dialog_title); + + if (!(editor instanceof CEditor)) { + IStatus status = new Status(IStatus.ERROR, CUIPlugin.PLUGIN_ID, ICStatusConstants.INTERNAL_ERROR, "Action not supportted outside the context of the C Editor", null); //$NON-NLS-1$ + throw new CoreException(status); + } + + fTranslationUnit = ((CEditor) editor).getInputCElement(); + fTextSelection = textSelection; + } + + @Override + protected IStatus run(IProgressMonitor monitor) { + try { + return resolve(monitor); + } catch (CoreException e) { + return e.getStatus(); + } + } + + IStatus resolve(IProgressMonitor monitor) throws CoreException { + assert fIndex == null; + if (fIndex != null) + return Status.CANCEL_STATUS; + + fIndex = CCorePlugin.getIndexManager() + .getIndex(fTranslationUnit.getCProject(), IIndexManager.ADD_DEPENDENCIES | IIndexManager.ADD_DEPENDENT | IIndexManager.ADD_EXTENSION_FRAGMENTS_NAVIGATION); + + try { + fIndex.acquireReadLock(); + } catch (InterruptedException e) { + return Status.CANCEL_STATUS; + } + + try { + return ASTProvider.getASTProvider().runOnAST(fTranslationUnit, ASTProvider.WAIT_NO, monitor, this); + } finally { + fIndex.releaseReadLock(); + } + } + + @Override + public IStatus runOnAST(ILanguage lang, IASTTranslationUnit ast) throws CoreException { + + // initialize to empty results + fResolvedFunctions = new IFunctionDeclaration[0]; + + if (ast == null) { + return Status.OK_STATUS; + } + + int selectionStart = fTextSelection.getOffset(); + int selectionLength = fTextSelection.getLength(); + + final IASTNodeSelector nodeSelector = ast.getNodeSelector(null); + + IASTName sourceName = nodeSelector.findEnclosingName(selectionStart, selectionLength); + if (sourceName == null) { + IStatus status = new Status(IStatus.ERROR, CUIPlugin.PLUGIN_ID, CEditorMessages.StepIntoSelection_unable_to_resolve_name); + throw new CoreException(status); + } + + IName[] implicitTargets = findImplicitTargets(ast, nodeSelector, selectionStart, selectionLength); + + NameKind kind = getNameKind(sourceName); + IBinding b = sourceName.resolveBinding(); + IBinding[] bindings = new IBinding[] { b }; + if (b instanceof IProblemBinding) { + IBinding[] candidateBindings = ((IProblemBinding) b).getCandidateBindings(); + if (candidateBindings.length != 0) { + bindings = candidateBindings; + } + } else if (kind == NameKind.DEFINITION && b instanceof IType) { + // No resolution of type definitions. + return Status.OK_STATUS; + } + + IName[] targets = IName.EMPTY_ARRAY; + String filename = ast.getFilePath(); + for (IBinding binding : bindings) { + if (binding != null && !(binding instanceof IProblemBinding)) { + IName[] names = findDeclNames(ast, kind, binding); + for (final IName name : names) { + if (name != null) { + if (name instanceof IIndexName && filename.equals(((IIndexName) name).getFileLocation().getFileName())) { + // Exclude index names from the current file. + } else if (areOverlappingNames(name, sourceName)) { + // Exclude the current location. + } else if (binding instanceof IParameter) { + if (isInSameFunction(sourceName, name)) { + targets = ArrayUtil.append(targets, name); + } + } else if (binding instanceof ICPPTemplateParameter) { + if (isInSameTemplate(sourceName, name)) { + targets = ArrayUtil.append(targets, name); + } + } else { + targets = ArrayUtil.append(targets, name); + } + } + } + } + } + + targets = ArrayUtil.trim(ArrayUtil.addAll(targets, implicitTargets)); + + final ArrayList<IFunctionDeclaration> functionElements = new ArrayList<IFunctionDeclaration>(); + filterToFunctions(fTranslationUnit.getCProject(), fIndex, targets, functionElements); + + // save the resolved function declarations + fResolvedFunctions = functionElements.toArray(new IFunctionDeclaration[functionElements.size()]); + + return Status.OK_STATUS; + } + + /** + * This is the method used to retrieve the results from this job + * @return The function(s) matching the selection + */ + public IFunctionDeclaration[] getSelectedFunctions() { + return fResolvedFunctions; + } + + private IName[] findDeclNames(IASTTranslationUnit ast, NameKind kind, IBinding binding) throws CoreException { + IName[] declNames = findNames(fIndex, ast, kind, binding); + // Bug 207320, handle template instances. + while (declNames.length == 0 && binding instanceof ICPPSpecialization) { + binding = ((ICPPSpecialization) binding).getSpecializedBinding(); + if (binding != null && !(binding instanceof IProblemBinding)) { + declNames = findNames(fIndex, ast, NameKind.DEFINITION, binding); + } + } + if (declNames.length == 0 && binding instanceof ICPPMethod) { + // Bug 86829, handle implicit methods. + ICPPMethod method = (ICPPMethod) binding; + if (method.isImplicit()) { + IBinding clsBinding = method.getClassOwner(); + if (clsBinding != null && !(clsBinding instanceof IProblemBinding)) { + declNames = findNames(fIndex, ast, NameKind.REFERENCE, clsBinding); + } + } + } + return declNames; + } + + private IName[] findNames(IIndex index, IASTTranslationUnit ast, NameKind kind, IBinding binding) throws CoreException { + IName[] declNames; + if (kind == NameKind.DEFINITION) { + declNames = findDeclarations(index, ast, binding); + } else { + declNames = findDefinitions(index, ast, kind, binding); + } + + if (declNames.length == 0) { + if (kind == NameKind.DEFINITION) { + declNames = findDefinitions(index, ast, kind, binding); + } else { + declNames = findDeclarations(index, ast, binding); + } + } + return declNames; + } + + private IName[] findDefinitions(IIndex index, IASTTranslationUnit ast, NameKind kind, IBinding binding) throws CoreException { + List<IASTName> declNames = new ArrayList<IASTName>(); + declNames.addAll(Arrays.asList(ast.getDefinitionsInAST(binding))); + for (Iterator<IASTName> i = declNames.iterator(); i.hasNext();) { + IASTName name = i.next(); + final IBinding b2 = name.resolveBinding(); + if (b2 instanceof ICPPUsingDeclaration) { + i.remove(); + } + if (binding != b2 && binding instanceof ICPPSpecialization) { + // Make sure binding specializes b2 so that for instance we do + // not navigate from + // one partial specialization to another. + IBinding spec = binding; + while (spec instanceof ICPPSpecialization) { + spec = ((ICPPSpecialization) spec).getSpecializedBinding(); + if (spec == b2) + break; + } + if (!(spec instanceof ICPPSpecialization)) { + i.remove(); + } + } + } + if (!declNames.isEmpty()) { + return declNames.toArray(new IASTName[declNames.size()]); + } + + // 2. Try definition in index. + return index.findNames(binding, IIndex.FIND_DEFINITIONS | IIndex.SEARCH_ACROSS_LANGUAGE_BOUNDARIES); + } + + private IName[] findDeclarations(IIndex index, IASTTranslationUnit ast, IBinding binding) throws CoreException { + IASTName[] astNames = ast.getDeclarationsInAST(binding); + ArrayList<IASTName> usingDeclarations = null; + for (int i = 0; i < astNames.length; i++) { + IASTName name = astNames[i]; + if (name.isDefinition()) { + astNames[i] = null; + } else if (CPPVisitor.findAncestorWithType(name, ICPPASTUsingDeclaration.class) != null) { + if (usingDeclarations == null) + usingDeclarations = new ArrayList<IASTName>(1); + usingDeclarations.add(name); + astNames[i] = null; + } + } + IName[] declNames = ArrayUtil.removeNulls(astNames); + if (declNames.length == 0) { + declNames = index.findNames(binding, IIndex.FIND_DECLARATIONS | IIndex.SEARCH_ACROSS_LANGUAGE_BOUNDARIES); + } + // 'using' declarations are considered only when there are no other + // declarations. + if (declNames.length == 0 && usingDeclarations != null) { + declNames = usingDeclarations.toArray(new IName[usingDeclarations.size()]); + } + return declNames; + } + + /** + * Returns definitions of bindings referenced by implicit name at the given location. + */ + private IName[] findImplicitTargets(IASTTranslationUnit ast, IASTNodeSelector nodeSelector, int offset, int length) throws CoreException { + IName[] definitions = IName.EMPTY_ARRAY; + IASTName firstName = nodeSelector.findEnclosingImplicitName(offset, length); + if (firstName != null) { + IASTImplicitNameOwner owner = (IASTImplicitNameOwner) firstName.getParent(); + for (IASTImplicitName name : owner.getImplicitNames()) { + if (((ASTNode) name).getOffset() == ((ASTNode) firstName).getOffset()) { + IBinding binding = name.resolveBinding(); // Guaranteed to + // resolve. + IName[] declNames = findDeclNames(ast, NameKind.REFERENCE, binding); + definitions = ArrayUtil.addAll(definitions, declNames); + } + } + } + return ArrayUtil.trim(definitions); + } + + private static NameKind getNameKind(IName name) { + if (name.isDefinition()) { + if (getBinding(name) instanceof ICPPUsingDeclaration) { + return NameKind.USING_DECL; + } else { + return NameKind.DEFINITION; + } + } else if (name.isDeclaration()) { + return NameKind.DECLARATION; + } + return NameKind.REFERENCE; + } + + private static IBinding getBinding(IName name) { + if (name instanceof IASTName) { + return ((IASTName) name).resolveBinding(); + } else if (name instanceof IIndexFragmentName) { + try { + return ((IIndexFragmentName) name).getBinding(); + } catch (CoreException e) { + // Fall through to return null. + } + } + return null; + } + + private boolean areOverlappingNames(IName n1, IName n2) { + if (n1 == n2) + return true; + + IASTFileLocation loc1 = n1.getFileLocation(); + IASTFileLocation loc2 = n2.getFileLocation(); + if (loc1 == null || loc2 == null) + return false; + return loc1.getFileName().equals(loc2.getFileName()) + && max(loc1.getNodeOffset(), loc2.getNodeOffset()) < min(loc1.getNodeOffset() + loc1.getNodeLength(), loc2.getNodeOffset() + loc2.getNodeLength()); + } + + private static boolean isInSameFunction(IASTName refName, IName funcDeclName) { + if (funcDeclName instanceof IASTName) { + IASTDeclaration fdef = getEnclosingFunctionDefinition((IASTNode) funcDeclName); + return fdef != null && fdef.contains(refName); + } + return false; + } + + private static IASTDeclaration getEnclosingFunctionDefinition(IASTNode node) { + while (node != null && !(node instanceof IASTFunctionDefinition)) { + node = node.getParent(); + } + return (IASTDeclaration) node; + } + + private static boolean isInSameTemplate(IASTName refName, IName templateDeclName) { + if (templateDeclName instanceof IASTName) { + IASTDeclaration template = getEnclosingTemplateDeclaration(refName); + return template != null && template.contains(refName); + } + return false; + } + + private static IASTDeclaration getEnclosingTemplateDeclaration(IASTNode node) { + while (node != null && !(node instanceof ICPPASTTemplateDeclaration)) { + node = node.getParent(); + } + return (IASTDeclaration) node; + } + + private void filterToFunctions(ICProject project, IIndex index, IName[] declNames, List<IFunctionDeclaration> functionElements) { + for (IName declName : declNames) { + try { + ICElement elem = getCElementForName(project, index, declName); + if (elem instanceof IFunctionDeclaration) { + functionElements.add((IFunctionDeclaration) elem); + } + } catch (CoreException e) { + CUIPlugin.log(e); + } + } + } + + private ICElementHandle getCElementForName(ICProject project, IIndex index, IName declName) throws CoreException { + if (declName instanceof IIndexName) { + return IndexUI.getCElementForName(project, index, (IIndexName) declName); + } + if (declName instanceof IASTName) { + IASTName astName = (IASTName) declName; + IBinding binding = astName.resolveBinding(); + if (binding != null) { + ITranslationUnit tu = IndexUI.getTranslationUnit(project, astName); + if (tu != null) { + IASTFileLocation loc = astName.getFileLocation(); + IRegion region = new Region(loc.getNodeOffset(), loc.getNodeLength()); + return CElementHandleFactory.create(tu, binding, astName.isDefinition(), region, 0); + } + } + return null; + } + return null; + } + +} diff --git a/debug/org.eclipse.cdt.debug.core/src/org/eclipse/cdt/debug/core/model/IStepIntoSelectionHandler.java b/debug/org.eclipse.cdt.debug.core/src/org/eclipse/cdt/debug/core/model/IStepIntoSelectionHandler.java new file mode 100644 index 00000000000..c3006dd509f --- /dev/null +++ b/debug/org.eclipse.cdt.debug.core/src/org/eclipse/cdt/debug/core/model/IStepIntoSelectionHandler.java @@ -0,0 +1,20 @@ +/******************************************************************************* + * Copyright (c) 2013 Ericsson AB 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: + * Alvaro Sanchez-Leon (Ericsson AB) - Support for Step into selection (bug 244865) + *******************************************************************************/ +package org.eclipse.cdt.debug.core.model; + +import org.eclipse.debug.core.commands.IDebugCommandHandler; + +/** + * @since 7.3 + * + */ +public interface IStepIntoSelectionHandler extends IDebugCommandHandler { +} diff --git a/debug/org.eclipse.cdt.debug.ui/plugin.properties b/debug/org.eclipse.cdt.debug.ui/plugin.properties index 2266d66e86e..0f70afeed03 100644 --- a/debug/org.eclipse.cdt.debug.ui/plugin.properties +++ b/debug/org.eclipse.cdt.debug.ui/plugin.properties @@ -12,6 +12,7 @@ # Dobrin Alexiev (Texas Instruments) - initial API and implementation (bug 336876) # Marc Khouzam (Ericsson) - Added support for connect command (Bug 365601) # Marc Dumais (Ericsson) - Added support for reverse debug action (Bug 365776) +# Alvaro Sanchez-Leon (Ericsson AB) - Support for Step into selection (bug 244865) ############################################################################### pluginName=C/C++ Development Tools Debugger UI @@ -248,3 +249,7 @@ DebugNewExecutable.name=Debug New Executable DebugNewExecutable.description=Debug a new executable DebugNewExecutable.label=Debug New Executable... DebugNewExecutable.tooltip=Debug a new executable + +# Step into selection +popup.stepIntoSelection.description=Step into the current selected statement +popup.stepIntoSelection.name=Step Into Selection diff --git a/debug/org.eclipse.cdt.debug.ui/plugin.xml b/debug/org.eclipse.cdt.debug.ui/plugin.xml index d2b3fc1b6f1..3ecc0ee12cd 100644 --- a/debug/org.eclipse.cdt.debug.ui/plugin.xml +++ b/debug/org.eclipse.cdt.debug.ui/plugin.xml @@ -2088,6 +2088,20 @@ class="org.eclipse.cdt.debug.internal.ui.commands.DebugNewExecutableHandler" commandId="org.eclipse.cdt.debug.ui.command.debugNewExecutable"> </handler> + <handler + class="org.eclipse.cdt.debug.internal.ui.commands.StepIntoSelectionCommandHandler" + commandId="org.eclipse.cdt.debug.ui.command.StepIntoSelection"> + <enabledWhen> + <and> + <with + variable="activeEditor"> + <instanceof + value="org.eclipse.ui.texteditor.ITextEditor"> + </instanceof> + </with> + </and> + </enabledWhen> + </handler> </extension> <extension point="org.eclipse.core.expressions.definitions"> @@ -2180,6 +2194,10 @@ schemeId="org.eclipse.ui.defaultAcceleratorConfiguration" sequence="SHIFT+F7"> </key> + <key sequence="M1+F5" + contextId="org.eclipse.cdt.debug.ui.debugging" + commandId="org.eclipse.cdt.debug.ui.command.StepIntoSelection" + schemeId="org.eclipse.ui.defaultAcceleratorConfiguration"/> </extension> <extension point="org.eclipse.ui.commands"> @@ -2191,6 +2209,13 @@ id="org.eclipse.cdt.debug.command.breakpointProperties" name="%BreakpointPropertiesCommand.name"> </command> + <command + categoryId="org.eclipse.debug.ui.category.run" + description="%popup.stepIntoSelection.description" + helpContextId="step_into_selection_action_context" + id="org.eclipse.cdt.debug.ui.command.StepIntoSelection" + name="%popup.stepIntoSelection.name"> + </command> </extension> <extension point="org.eclipse.ui.menus"> @@ -2572,6 +2597,52 @@ style="push" tooltip="%Connect.tooltip"> </command> + + <!-- Step Into Selection debugging contributions --> + </menuContribution> + <menuContribution + locationURI="menu:org.eclipse.ui.run?endof=emptyStepGroup"> + <command + commandId="org.eclipse.cdt.debug.ui.command.StepIntoSelection" + style="push"> + <visibleWhen + checkEnabled="false"> + <and> + <systemTest + property="org.eclipse.cdt.debug.ui.debuggerActive" + value="true"> + </systemTest> + <with + variable="activeEditor"> + <instanceof + value="org.eclipse.ui.texteditor.ITextEditor"> + </instanceof> + </with> + </and> + </visibleWhen> + </command> + </menuContribution> + <menuContribution + locationURI="popup:#CEditorContext?before=additions"> + <command + commandId="org.eclipse.cdt.debug.ui.command.StepIntoSelection" + style="push"> + <visibleWhen + checkEnabled="false"> + <and> + <systemTest + property="org.eclipse.cdt.debug.ui.debuggerActive" + value="true"> + </systemTest> + <with + variable="activeEditor"> + <instanceof + value="org.eclipse.ui.texteditor.ITextEditor"> + </instanceof> + </with> + </and> + </visibleWhen> + </command> </menuContribution> </extension> diff --git a/debug/org.eclipse.cdt.debug.ui/src/org/eclipse/cdt/debug/internal/ui/commands/StepIntoSelectionCommandHandler.java b/debug/org.eclipse.cdt.debug.ui/src/org/eclipse/cdt/debug/internal/ui/commands/StepIntoSelectionCommandHandler.java new file mode 100644 index 00000000000..f9a9b988cc2 --- /dev/null +++ b/debug/org.eclipse.cdt.debug.ui/src/org/eclipse/cdt/debug/internal/ui/commands/StepIntoSelectionCommandHandler.java @@ -0,0 +1,22 @@ +/******************************************************************************* + * Copyright (c) 2013 Ericsson AB 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: + * Alvaro Sanchez-Leon (Ericsson AB) - Support for Step into selection (bug 244865) + *******************************************************************************/ +package org.eclipse.cdt.debug.internal.ui.commands; + +import org.eclipse.cdt.debug.core.model.IStepIntoSelectionHandler; +import org.eclipse.debug.ui.actions.DebugCommandHandler; + + +public class StepIntoSelectionCommandHandler extends DebugCommandHandler { + @Override + protected Class<?> getCommandType() { + return IStepIntoSelectionHandler.class; + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/GdbAdapterFactory.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/GdbAdapterFactory.java index 51d5f2ea88b..fb440c46362 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/GdbAdapterFactory.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/GdbAdapterFactory.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 @@ -9,6 +9,7 @@ * Wind River Systems - initial API and implementation * Navid Mehregani (TI) - Bug 289526 - Migrate the Restart feature to the new one, as supported by the platform * Patrick Chuong (Texas Instruments) - Add support for icon overlay in the debug view (Bug 334566) + * Alvaro Sanchez-Leon (Ericsson AB) - Support for Step into selection (bug 244865) *******************************************************************************/ package org.eclipse.cdt.dsf.gdb.internal.ui; @@ -27,6 +28,7 @@ import org.eclipse.cdt.debug.core.model.IReverseStepOverHandler; import org.eclipse.cdt.debug.core.model.IReverseToggleHandler; import org.eclipse.cdt.debug.core.model.ISaveTraceDataHandler; import org.eclipse.cdt.debug.core.model.IStartTracingHandler; +import org.eclipse.cdt.debug.core.model.IStepIntoSelectionHandler; import org.eclipse.cdt.debug.core.model.ISteppingModeTarget; import org.eclipse.cdt.debug.core.model.IStopTracingHandler; import org.eclipse.cdt.debug.core.model.IUncallHandler; @@ -35,9 +37,11 @@ import org.eclipse.cdt.dsf.concurrent.Immutable; import org.eclipse.cdt.dsf.concurrent.ThreadSafe; import org.eclipse.cdt.dsf.debug.ui.actions.DsfResumeCommand; import org.eclipse.cdt.dsf.debug.ui.actions.DsfStepIntoCommand; +import org.eclipse.cdt.dsf.debug.ui.actions.DsfStepIntoSelectionCommand; import org.eclipse.cdt.dsf.debug.ui.actions.DsfStepOverCommand; import org.eclipse.cdt.dsf.debug.ui.actions.DsfStepReturnCommand; import org.eclipse.cdt.dsf.debug.ui.actions.DsfSuspendCommand; +import org.eclipse.cdt.dsf.debug.ui.actions.IDsfStepIntoSelection; import org.eclipse.cdt.dsf.debug.ui.sourcelookup.DsfSourceDisplayAdapter; import org.eclipse.cdt.dsf.debug.ui.viewmodel.SteppingController; import org.eclipse.cdt.dsf.debug.ui.viewmodel.actions.DefaultRefreshAllTarget; @@ -105,6 +109,7 @@ public class GdbAdapterFactory final GdbViewModelAdapter fViewModelAdapter; final DsfSourceDisplayAdapter fSourceDisplayAdapter; final DsfStepIntoCommand fStepIntoCommand; + final DsfStepIntoSelectionCommand fStepIntoSelectionCommand; final GdbReverseStepIntoCommand fReverseStepIntoCommand; final DsfStepOverCommand fStepOverCommand; final GdbReverseStepOverCommand fReverseStepOverCommand; @@ -154,6 +159,7 @@ public class GdbAdapterFactory fSteppingModeTarget = new GdbSteppingModeTarget(session); fStepIntoCommand = new DsfStepIntoCommand(session, fSteppingModeTarget); + fStepIntoSelectionCommand = new DsfStepIntoSelectionCommand(session); fReverseStepIntoCommand = new GdbReverseStepIntoCommand(session, fSteppingModeTarget); fStepOverCommand = new DsfStepOverCommand(session, fSteppingModeTarget); fReverseStepOverCommand = new GdbReverseStepOverCommand(session, fSteppingModeTarget); @@ -181,6 +187,7 @@ public class GdbAdapterFactory session.registerModelAdapter(ISteppingModeTarget.class, fSteppingModeTarget); session.registerModelAdapter(IStepIntoHandler.class, fStepIntoCommand); + session.registerModelAdapter(IStepIntoSelectionHandler.class, fStepIntoSelectionCommand); session.registerModelAdapter(IReverseStepIntoHandler.class, fReverseStepIntoCommand); session.registerModelAdapter(IStepOverHandler.class, fStepOverCommand); session.registerModelAdapter(IReverseStepOverHandler.class, fReverseStepOverCommand); @@ -204,6 +211,7 @@ public class GdbAdapterFactory session.registerModelAdapter(ISelectNextTraceRecordHandler.class, fSelectNextRecordTarget); session.registerModelAdapter(ISelectPrevTraceRecordHandler.class, fSelectPrevRecordTarget); session.registerModelAdapter(IPinProvider.class, fPinProvider); + session.registerModelAdapter(IDsfStepIntoSelection.class, fStepIntoSelectionCommand); fDebugModelProvider = new IDebugModelProvider() { // @see org.eclipse.debug.core.model.IDebugModelProvider#getModelIdentifiers() @@ -242,6 +250,7 @@ public class GdbAdapterFactory session.unregisterModelAdapter(ISteppingModeTarget.class); session.unregisterModelAdapter(IStepIntoHandler.class); + session.unregisterModelAdapter(IStepIntoSelectionHandler.class); session.unregisterModelAdapter(IReverseStepIntoHandler.class); session.unregisterModelAdapter(IStepOverHandler.class); session.unregisterModelAdapter(IReverseStepOverHandler.class); @@ -273,6 +282,7 @@ public class GdbAdapterFactory fSteppingModeTarget.dispose(); fStepIntoCommand.dispose(); + fStepIntoSelectionCommand.dispose(); fReverseStepIntoCommand.dispose(); fStepOverCommand.dispose(); fReverseStepOverCommand.dispose(); diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/META-INF/MANIFEST.MF b/dsf-gdb/org.eclipse.cdt.dsf.gdb/META-INF/MANIFEST.MF index 875fd3ece60..59bbcce538b 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/META-INF/MANIFEST.MF +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/META-INF/MANIFEST.MF @@ -23,6 +23,7 @@ Export-Package: org.eclipse.cdt.dsf.gdb, org.eclipse.cdt.dsf.gdb.internal;x-friends:="org.eclipse.cdt.dsf.gdb.ui,org.eclipse.cdt.debug.gdbjtag.ui", org.eclipse.cdt.dsf.gdb.internal.commands;x-friends:="org.eclipse.cdt.dsf.gdb.ui", org.eclipse.cdt.dsf.gdb.internal.memory;x-internal:=true, + org.eclipse.cdt.dsf.gdb.internal.service.control;x-internal:=true, org.eclipse.cdt.dsf.gdb.internal.service.command.events;x-internal:=true, org.eclipse.cdt.dsf.gdb.internal.tracepointactions;x-friends:="org.eclipse.cdt.dsf.gdb.ui", org.eclipse.cdt.dsf.gdb.launching, diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/internal/Messages.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/internal/Messages.java index ee19d3c3678..60fd44bb351 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/internal/Messages.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/internal/Messages.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2011 Mentor Graphics and others. + * Copyright (c) 2011, 2013 Mentor Graphics 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 @@ -7,6 +7,7 @@ * * Contributors: * Mentor Graphics - Initial API and implementation + * Alvaro Sanchez-Leon (Ericsson AB) - Support for Step into selection *******************************************************************************/ package org.eclipse.cdt.dsf.gdb.internal; @@ -22,6 +23,11 @@ public class Messages extends NLS { public static String CustomTimeoutsMap_Invalid_custom_timeout_value; public static String GDBControl_Session_is_terminated; + + public static String StepIntoSelection; + + public static String StepIntoSelection_Execution_did_not_enter_function; + static { // initialize resource bundle NLS.initializeMessages( Messages.class.getName(), Messages.class ); diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/internal/Messages.properties b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/internal/Messages.properties index 9a4e8c3dbbe..978adaa94e6 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/internal/Messages.properties +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/internal/Messages.properties @@ -1,5 +1,5 @@ ####################################################################################### -# Copyright (c) 2011 Mentor Graphics and others. +# Copyright (c) 2011, 2013 Mentor Graphics 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 @@ -7,9 +7,12 @@ # # Contributors: # Mentor Graphics - Initial API and implementation +# Alvaro Sanchez-Leon (Ericsson AB) - Support for Step into selection ####################################################################################### CustomTimeoutsMap_Error_initializing_custom_timeouts=Error initializing custom timeouts CustomTimeoutsMap_Invalid_custom_timeout_data=Invalid custom timeout data. CustomTimeoutsMap_Invalid_custom_timeout_value=Invalid custom timeout value for '%s'. GDBControl_Session_is_terminated=Session is terminated.\nReason: %s +StepIntoSelection=Step into selection +StepIntoSelection_Execution_did_not_enter_function=Execution did not enter \"{0}\" diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/internal/service/control/StepIntoSelectionActiveOperation.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/internal/service/control/StepIntoSelectionActiveOperation.java new file mode 100644 index 00000000000..8f9e5c29e11 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/internal/service/control/StepIntoSelectionActiveOperation.java @@ -0,0 +1,101 @@ +/******************************************************************************* + * Copyright (c) 2013 Ericsson AB 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: + * Alvaro Sanchez-Leon (Ericsson AB) - Support for Step into selection (bug 244865) + *******************************************************************************/ +package org.eclipse.cdt.dsf.gdb.internal.service.control; + +import org.eclipse.cdt.core.model.IFunctionDeclaration; +import org.eclipse.cdt.dsf.mi.service.IMIExecutionDMContext; +import org.eclipse.cdt.dsf.mi.service.command.output.MIFrame; + +/** + * @since 4.2 + */ +public class StepIntoSelectionActiveOperation { + private final IFunctionDeclaration fTargetFunction; + private final IMIExecutionDMContext fThreadContext; + private String fBaseFileLocation = null; + private int fBaseLine = 0; + private int fOriginalStackDepth=0; + private String fFunctionSignature = null; + private MIFrame fRunToLineFrame = null; + + public StepIntoSelectionActiveOperation(IMIExecutionDMContext threadContext, int line, IFunctionDeclaration targetFunction, + int stackDepth, MIFrame runToLineFrame) { + fThreadContext = threadContext; + fBaseLine = line; + fTargetFunction = targetFunction; + fOriginalStackDepth = stackDepth; + + fRunToLineFrame = runToLineFrame; + init(); + } + + private void init() { + if (fRunToLineFrame == null) { + return; + } + + fBaseFileLocation = fRunToLineFrame.getFile() + ":" + fBaseLine; //$NON-NLS-1$ + } + + public IFunctionDeclaration getTargetFunctionDeclaration() { + return fTargetFunction; + } + + public IMIExecutionDMContext getThreadContext() { + return fThreadContext; + } + + public String getFileLocation() { + return fBaseFileLocation; + } + + public int getLine() { + return fBaseLine; + } + + public int getOriginalStackDepth() { + return fOriginalStackDepth; + } + + public void setOriginalStackDepth(Integer originalStackDepth) { + fOriginalStackDepth = originalStackDepth; + } + + public MIFrame getRunToLineFrame() { + return fRunToLineFrame; + } + + public void setRunToLineFrame(MIFrame runToLineFrame) { + if (runToLineFrame != null) { + fRunToLineFrame = runToLineFrame; + init(); + } + } + + public String getTargetFunctionSignature() { + if (fFunctionSignature != null) { + return fFunctionSignature; + } else { + if (fTargetFunction != null) { + StringBuilder sb = null; + sb = new StringBuilder(); + if (fTargetFunction.getParent() != null) { + sb.append(fTargetFunction.getParent().getElementName() + StepIntoSelectionUtils.cppSep); + } + + sb.append(fTargetFunction.getElementName()); + fFunctionSignature = sb.toString(); + } + } + + return fFunctionSignature; + } +}
\ No newline at end of file diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/internal/service/control/StepIntoSelectionUtils.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/internal/service/control/StepIntoSelectionUtils.java new file mode 100644 index 00000000000..590404dde97 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/internal/service/control/StepIntoSelectionUtils.java @@ -0,0 +1,93 @@ +/******************************************************************************* + * Copyright (c) 2013 Ericsson AB 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: + * Alvaro Sanchez-Leon (Ericsson AB) - Support for Step into selection (bug 244865) + *******************************************************************************/ + +package org.eclipse.cdt.dsf.gdb.internal.service.control; + +import org.eclipse.cdt.dsf.gdb.IGdbDebugConstants; +import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin; +import org.eclipse.cdt.dsf.gdb.internal.Messages; +import org.eclipse.cdt.dsf.mi.service.command.output.MIArg; +import org.eclipse.cdt.dsf.mi.service.command.output.MIFrame; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.debug.core.DebugPlugin; +import org.eclipse.debug.core.IStatusHandler; +import org.eclipse.osgi.util.NLS; + +public class StepIntoSelectionUtils { + + public static final String cppSep = "::"; //$NON-NLS-1$ + + public static boolean sameSignature(MIFrame currentFrame, StepIntoSelectionActiveOperation stepOperation) { + String currentFunctionName = currentFrame.getFunction(); + String targetFunctionName = stepOperation.getTargetFunctionSignature(); + + if (!sameNumberOfArgumets(currentFrame, stepOperation)) { + return false; + } + + // Simplified validation + if (currentFunctionName.equals(targetFunctionName)) { + return true; + } + + // Check if the last segment of the function name match + String[] currentFunctTokens = currentFunctionName.split(cppSep); + String[] targetFunctTokens = targetFunctionName.split(cppSep); + if (currentFunctTokens.length > 1) { + currentFunctionName = currentFunctTokens[currentFunctTokens.length - 1]; + } + + if (targetFunctTokens.length > 1) { + targetFunctionName = targetFunctTokens[targetFunctTokens.length - 1]; + } + + if (currentFunctionName.equals(targetFunctionName)) { + // Function name matches and parent name does not. One of the parents may be a super class + // Simple enough for initial implementation. + return true; + } + + // TODO: A more detailed check can be implemented in order to cover for parameter types return types, etc.. + // This with the intention to avoid early stops, however this implementation need to be tested extensively in + // order to avoid missing the target due to unexpected formatting mismatch between declaration and GDB representation. + + return false; + } + + private static boolean sameNumberOfArgumets(MIFrame currentFrame, StepIntoSelectionActiveOperation stepOperation) { + int argSizeAdjustment = 0; + MIArg[] args = currentFrame.getArgs(); + if (args.length > 0) { + // GDB may add the argument "this" e.g. in c++ programs + if (args[0].getName().equals("this")) { //$NON-NLS-1$ + argSizeAdjustment = 1; + } + } + + return ((currentFrame.getArgs().length - argSizeAdjustment) == stepOperation.getTargetFunctionDeclaration().getNumberOfParameters()); + } + + public static void missedSelectedTarget(StepIntoSelectionActiveOperation stepOperation) { + final String functionName = stepOperation.getTargetFunctionDeclaration().getElementName(); + IStatus status = new Status(IStatus.INFO, GdbPlugin.PLUGIN_ID, IGdbDebugConstants.STATUS_HANDLER_CODE, Messages.StepIntoSelection + "\n" + NLS.bind(Messages.StepIntoSelection_Execution_did_not_enter_function, functionName), null); //$NON-NLS-1$ + IStatusHandler statusHandler = DebugPlugin.getDefault().getStatusHandler(status); + if (statusHandler != null) { + try { + statusHandler.handleStatus(status, null); + } catch (CoreException ex) { + GdbPlugin.getDefault().getLog().log(ex.getStatus()); + } + } + } + +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBRunControl.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBRunControl.java index c8fa3ba586b..d8bbebf4277 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBRunControl.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBRunControl.java @@ -9,6 +9,7 @@ * Wind River Systems - initial API and implementation * Ericsson AB - Modified for additional functionality * Nokia - create and use backend service. + * Alvaro Sanchez-Leon (Ericsson AB) - Support for Step into selection (bug 244865) *******************************************************************************/ package org.eclipse.cdt.dsf.gdb.service; @@ -19,7 +20,9 @@ import java.util.Hashtable; import java.util.Iterator; import java.util.List; +import org.eclipse.cdt.core.model.IFunctionDeclaration; import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.ImmediateDataRequestMonitor; import org.eclipse.cdt.dsf.concurrent.ImmediateRequestMonitor; import org.eclipse.cdt.dsf.concurrent.RequestMonitor; import org.eclipse.cdt.dsf.datamodel.DMContexts; @@ -29,7 +32,11 @@ import org.eclipse.cdt.dsf.debug.service.IProcesses.IProcessDMContext; import org.eclipse.cdt.dsf.debug.service.IProcesses.IThreadDMContext; import org.eclipse.cdt.dsf.debug.service.IRunControl; import org.eclipse.cdt.dsf.debug.service.IRunControl2; +import org.eclipse.cdt.dsf.debug.service.IRunControl3; +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService; import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin; +import org.eclipse.cdt.dsf.gdb.internal.service.control.StepIntoSelectionActiveOperation; +import org.eclipse.cdt.dsf.gdb.internal.service.control.StepIntoSelectionUtils; import org.eclipse.cdt.dsf.mi.service.IMICommandControl; import org.eclipse.cdt.dsf.mi.service.IMIExecutionDMContext; import org.eclipse.cdt.dsf.mi.service.IMIProcessDMContext; @@ -46,6 +53,7 @@ import org.eclipse.cdt.dsf.mi.service.command.events.MIThreadExitEvent; import org.eclipse.cdt.dsf.mi.service.command.output.MIBreakInsertInfo; import org.eclipse.cdt.dsf.mi.service.command.output.MIFrame; import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo; +import org.eclipse.cdt.dsf.mi.service.command.output.MIStackInfoDepthInfo; import org.eclipse.cdt.dsf.service.DsfServiceEventHandler; import org.eclipse.cdt.dsf.service.DsfSession; import org.eclipse.core.runtime.IStatus; @@ -80,13 +88,15 @@ public class GDBRunControl extends MIRunControl { private IGDBBackend fGdb; private IMIProcesses fProcService; private CommandFactory fCommandFactory; + private ICommandControlService fConnection; // Record list of execution contexts private IExecutionDMContext[] fOldExecutionCtxts; private RunToLineActiveOperation fRunToLineActiveOperation = null; - + private StepIntoSelectionActiveOperation fStepInToSelectionActiveOperation = null; + public GDBRunControl(DsfSession session) { super(session); } @@ -106,9 +116,11 @@ public class GDBRunControl extends MIRunControl { fGdb = getServicesTracker().getService(IGDBBackend.class); fProcService = getServicesTracker().getService(IMIProcesses.class); fCommandFactory = getServicesTracker().getService(IMICommandControl.class).getCommandFactory(); + fConnection = getServicesTracker().getService(ICommandControlService.class); register(new String[]{IRunControl.class.getName(), IRunControl2.class.getName(), + IRunControl3.class.getName(), IMIRunControl.class.getName(), MIRunControl.class.getName(), GDBRunControl.class.getName()}, new Hashtable<String,String>()); @@ -332,6 +344,7 @@ public class GDBRunControl extends MIRunControl { getConnection().queueCommand(fCommandFactory.createMIBreakDelete(bpDmc, new int[] {bpId}), new DataRequestMonitor<MIInfo>(getExecutor(), null)); fRunToLineActiveOperation = null; + fStepInToSelectionActiveOperation = null; super.handleFailure(); } @@ -361,13 +374,26 @@ public class GDBRunControl extends MIRunControl { getConnection().queueCommand(fCommandFactory.createMIBreakDelete(bpDmc, new int[] {bpId}), new DataRequestMonitor<MIInfo>(getExecutor(), null)); fRunToLineActiveOperation = null; + fStepInToSelectionActiveOperation = null; } } /** @since 2.0 */ @Override @DsfServiceEventHandler - public void eventDispatched(final MIStoppedEvent e) { + public void eventDispatched(final MIStoppedEvent e) { + if (processRunToLineStoppedEvent(e)) { + // If RunToLine is not completed + return; + } + + if (!processStepIntoSelection(e)) { + //Step into Selection is not in progress broadcast the stop event + super.eventDispatched(e); + } + } + + private boolean processRunToLineStoppedEvent(final MIStoppedEvent e) { if (fRunToLineActiveOperation != null) { int bpId = 0; if (e instanceof MIBreakpointHitEvent) { @@ -407,7 +433,7 @@ public class GDBRunControl extends MIRunControl { new DataRequestMonitor<MIInfo>(getExecutor(), null)); // Don't send the stop event since we are resuming again. - return; + return true; } else { // Stopped at another breakpoint that we should not skip. // Or got an interrupt signal from a suspend command. @@ -423,11 +449,119 @@ public class GDBRunControl extends MIRunControl { getConnection().queueCommand(fCommandFactory.createMIBreakDelete(bpDmc, new int[] {fRunToLineActiveOperation.getBreakointId()}), new DataRequestMonitor<MIInfo>(getExecutor(), null)); fRunToLineActiveOperation = null; + fStepInToSelectionActiveOperation = null; } } } + + return false; + } + + private boolean processStepIntoSelection(final MIStoppedEvent e) { + if (fStepInToSelectionActiveOperation == null) { + return false; + } + + // First check if it is the right thread that stopped + final IMIExecutionDMContext threadDmc = DMContexts.getAncestorOfType(e.getDMContext(), IMIExecutionDMContext.class); + if (fStepInToSelectionActiveOperation.getThreadContext().equals(threadDmc)) { + final MIFrame frame = e.getFrame(); + + assert(fStepInToSelectionActiveOperation.getLine() == frame.getLine()); + assert(fRunToLineActiveOperation == null); + + if (fStepInToSelectionActiveOperation.getRunToLineFrame() == null) { + // Shall now be at the runToline location + fStepInToSelectionActiveOperation.setRunToLineFrame(frame); + } + + // Step - Not at the right place just yet + // Initiate an async call chain parent + getStackDepth(threadDmc, new DataRequestMonitor<Integer>(getExecutor(), null) { + private int originalStackDepth = fStepInToSelectionActiveOperation.getOriginalStackDepth(); + + @Override + protected void handleSuccess() { + int frameDepth = getStackDepth(); + + if (frameDepth > originalStackDepth) { + //shall be true as this is using stepinto step type vs instruction stepinto + assert(frameDepth == originalStackDepth + 1); + + // Check for a match + if (StepIntoSelectionUtils.sameSignature(frame, fStepInToSelectionActiveOperation)) { + // Hit !! + stopStepIntoSelection(e); + return; + } + + // Located deeper in the stack, Shall continue step / search + // Step return + continueStepping(e, StepType.STEP_RETURN); + } else if (frameDepth == originalStackDepth) { + // Continue step / search as long as + // this is the starting base line for the search + String currentLocation = frame.getFile() + ":" + frame.getLine(); //$NON-NLS-1$ + String searchLineLocation = fStepInToSelectionActiveOperation.getFileLocation(); + if (currentLocation.equals(searchLineLocation)) { + continueStepping(e, StepType.STEP_INTO); + } else { + // We have moved to a line + // different from the base + // search line i.e. missed the + // target function !! + StepIntoSelectionUtils.missedSelectedTarget(fStepInToSelectionActiveOperation); + stopStepIntoSelection(e); + } + } else { + // missed the target point + StepIntoSelectionUtils.missedSelectedTarget(fStepInToSelectionActiveOperation); } + } + + @Override + protected void handleFailure() { + // log error + if (getStatus() != null) { + GdbPlugin.getDefault().getLog().log(getStatus()); + } + + stopStepIntoSelection(e); + } + + private int getStackDepth() { + Integer stackDepth = null; + if (isSuccess() && getData() != null) { + stackDepth = getData(); + // This is the base frame, the original stack depth shall be updated + if (frame == fStepInToSelectionActiveOperation.getRunToLineFrame()) { + fStepInToSelectionActiveOperation.setOriginalStackDepth(stackDepth); + originalStackDepth = stackDepth; + } + } - super.eventDispatched(e); + if (stackDepth == null) { + // Unsuccessful resolution of stack depth, default to same stack depth to detect a change of line within the original frame + return fStepInToSelectionActiveOperation.getOriginalStackDepth(); + } + + return stackDepth.intValue(); + } + }); + + //Processing step into selection + return true; + } + + //All threads stopped, however outside the scope of the step into selection context + //We need to abort the step into selection and broadcast the stop + fStepInToSelectionActiveOperation = null; + return false; + } + + private void stopStepIntoSelection(final MIStoppedEvent e) { + fStepInToSelectionActiveOperation = null; + // Need to broadcast the stop + super.eventDispatched(e); } /** @@ -435,5 +569,134 @@ public class GDBRunControl extends MIRunControl { */ protected int getInterruptTimeout() { return IGDBBackend.INTERRUPT_TIMEOUT_DEFAULT; - } + } + + private void continueStepping(final MIStoppedEvent event, StepType steptype) { + step(fStepInToSelectionActiveOperation.getThreadContext(), steptype, false, new RequestMonitor(getExecutor(), null) { + @Override + protected void handleFailure() { + // log error + if (getStatus() != null) { + GdbPlugin.getDefault().getLog().log(getStatus()); + } + + stopStepIntoSelection(event); + } + }); + } + + // ------------------------------------------------------------------------ + // Step into Selection + // ------------------------------------------------------------------------ + private void stepIntoSelection(final IExecutionDMContext context, final int baseLine, final String baseLineLocation, final boolean skipBreakpoints, final IFunctionDeclaration targetFunction, + final RequestMonitor rm) { + + assert context != null; + + final IMIExecutionDMContext dmc = DMContexts.getAncestorOfType(context, IMIExecutionDMContext.class); + if (dmc == null) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Given context: " + context + " is not an MI execution context.", null)); //$NON-NLS-1$ //$NON-NLS-2$ + rm.done(); + return; + } + + if (!doCanResume(dmc)) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_STATE, "Cannot resume context", null)); //$NON-NLS-1$ + rm.done(); + return; + } + + if (fLatestEvent == null || !(fLatestEvent instanceof SuspendedEvent)) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_STATE, "Given context: " + context + " invalid suspended event.", null)); //$NON-NLS-1$ //$NON-NLS-2$ + rm.done(); + return; + } + + SuspendedEvent suspendedEvent = (SuspendedEvent) fLatestEvent; + final MIFrame currentFrame = suspendedEvent.getMIEvent().getFrame(); + if (currentFrame == null) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_STATE, "Given event: " + suspendedEvent + " with invalid frame.", null)); //$NON-NLS-1$ //$NON-NLS-2$ + rm.done(); + return; + } + + getStackDepth(dmc, new DataRequestMonitor<Integer>(getExecutor(), rm) { + @Override + public void handleSuccess() { + if (getData() != null) { + final int framesSize = getData().intValue(); + + // make sure the operation is removed upon + // failure detection + final RequestMonitor rms = new RequestMonitor(getExecutor(), rm) { + @Override + protected void handleFailure() { + fStepInToSelectionActiveOperation = null; + super.handleFailure(); + } + }; + + if ((currentFrame.getFile() + ":" + currentFrame.getLine()).endsWith(baseLineLocation)) { //$NON-NLS-1$ + // Save the step into selection information + fStepInToSelectionActiveOperation = new StepIntoSelectionActiveOperation(dmc, baseLine, targetFunction, framesSize, + currentFrame); + // Ready to step into a function selected + // within a current line + step(dmc, StepType.STEP_INTO, rms); + } else { + // Save the step into selection information + fStepInToSelectionActiveOperation = new StepIntoSelectionActiveOperation(dmc, baseLine, targetFunction, framesSize, null); + // Pointing to a line different than the current line + // Needs to RunToLine before stepping to the selection + runToLocation(dmc, baseLineLocation, skipBreakpoints, rms); + } + } else { + rm.done(); + } + } + }); + } + + /** + * @since 4.2 + */ + @Override + public void canStepIntoSelection(IExecutionDMContext context, String sourceFile, int lineNumber, IFunctionDeclaration selectedFunction, DataRequestMonitor<Boolean> rm) { + canStep(context, StepType.STEP_INTO, rm); + } + + /** + * @since 4.2 + */ + @Override + public void stepIntoSelection(final IExecutionDMContext context, String sourceFile, final int lineNumber, final boolean skipBreakpoints, final IFunctionDeclaration selectedFunction, final RequestMonitor rm) { + determineDebuggerPath(context, sourceFile, new ImmediateDataRequestMonitor<String>(rm) { + @Override + protected void handleSuccess() { + stepIntoSelection(context, lineNumber, getData() + ":" + Integer.toString(lineNumber), skipBreakpoints, selectedFunction, rm); //$NON-NLS-1$ + } + }); + } + + /** + * Help method used when the stopped event has not been broadcasted e.g. in the middle of step into selection + * + * @param dmc + * @param rm + */ + private void getStackDepth(final IMIExecutionDMContext dmc, final DataRequestMonitor<Integer> rm) { + if (dmc != null) { + fConnection.queueCommand(fCommandFactory.createMIStackInfoDepth(dmc), new DataRequestMonitor<MIStackInfoDepthInfo>(getExecutor(), rm) { + @Override + protected void handleSuccess() { + rm.setData(getData().getDepth()); + rm.done(); + } + }); + } else { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, "Invalid context", null)); //$NON-NLS-1$ + rm.done(); + } + } + } diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBRunControl_7_0_NS.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBRunControl_7_0_NS.java index a3d72580739..b4361367d53 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBRunControl_7_0_NS.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBRunControl_7_0_NS.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2006, 2012 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 @@ -10,6 +10,7 @@ * Ericsson AB - Modified for handling of multiple threads * Indel AG - [369622] fixed moveToLine using MinGW * Marc Khouzam (Ericsson) - Support for operations on multiple execution contexts (bug 330974) + * Alvaro Sanchez-Leon (Ericsson AB) - Support for Step into selection (bug 244865) *******************************************************************************/ package org.eclipse.cdt.dsf.gdb.service; @@ -25,6 +26,7 @@ import java.util.Map; import java.util.Set; import org.eclipse.cdt.core.IAddress; +import org.eclipse.cdt.core.model.IFunctionDeclaration; import org.eclipse.cdt.dsf.concurrent.CountingRequestMonitor; import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants; @@ -51,6 +53,7 @@ import org.eclipse.cdt.dsf.debug.service.IProcesses.IProcessDMContext; import org.eclipse.cdt.dsf.debug.service.IProcesses.IThreadDMContext; import org.eclipse.cdt.dsf.debug.service.IRunControl; import org.eclipse.cdt.dsf.debug.service.IRunControl2; +import org.eclipse.cdt.dsf.debug.service.IRunControl3; import org.eclipse.cdt.dsf.debug.service.ISourceLookup; import org.eclipse.cdt.dsf.debug.service.ISourceLookup.ISourceLookupDMContext; import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMContext; @@ -59,6 +62,8 @@ import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService; import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlShutdownDMEvent; import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin; import org.eclipse.cdt.dsf.gdb.internal.service.command.events.MITracepointSelectedEvent; +import org.eclipse.cdt.dsf.gdb.internal.service.control.StepIntoSelectionActiveOperation; +import org.eclipse.cdt.dsf.gdb.internal.service.control.StepIntoSelectionUtils; import org.eclipse.cdt.dsf.gdb.service.IGDBTraceControl.ITraceRecordSelectedChangedDMEvent; import org.eclipse.cdt.dsf.mi.service.IMICommandControl; import org.eclipse.cdt.dsf.mi.service.IMIContainerDMContext; @@ -87,7 +92,9 @@ import org.eclipse.cdt.dsf.mi.service.command.events.MIThreadCreatedEvent; import org.eclipse.cdt.dsf.mi.service.command.events.MIThreadExitEvent; import org.eclipse.cdt.dsf.mi.service.command.events.MIWatchpointTriggerEvent; import org.eclipse.cdt.dsf.mi.service.command.output.MIBreakInsertInfo; +import org.eclipse.cdt.dsf.mi.service.command.output.MIFrame; import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo; +import org.eclipse.cdt.dsf.mi.service.command.output.MIStackInfoDepthInfo; import org.eclipse.cdt.dsf.mi.service.command.output.MIThread; import org.eclipse.cdt.dsf.mi.service.command.output.MIThreadInfoInfo; import org.eclipse.cdt.dsf.service.AbstractDsfService; @@ -111,8 +118,11 @@ import org.osgi.framework.BundleContext; * sync with the service state. * @since 1.1 */ -public class GDBRunControl_7_0_NS extends AbstractDsfService implements IMIRunControl, IMultiRunControl, ICachingService -{ +public class GDBRunControl_7_0_NS extends AbstractDsfService implements IMIRunControl, IMultiRunControl, ICachingService, IRunControl3 { + // ///////////////////////////////////////////////////////////////////////// + // CONSTANTS + // ///////////////////////////////////////////////////////////////////////// + @Immutable private static class ExecutionData implements IExecutionDMData2 { private final StateChangeReason fReason; @@ -295,6 +305,7 @@ public class GDBRunControl_7_0_NS extends AbstractDsfService implements IMIRunCo boolean fSuspended = false; boolean fResumePending = false; boolean fStepping = false; + RunControlEvent<IExecutionDMContext, ?> fLatestEvent = null; /** * What caused the state change. E.g., a signal was thrown. @@ -336,7 +347,7 @@ public class GDBRunControl_7_0_NS extends AbstractDsfService implements IMIRunCo public boolean shouldSkipBreakpoints() { return fSkipBreakpoints; } } - /////////////////////////////////////////////////////////////////////////// + // ///////////////////////////////////////////////////////////////////////// // MIRunControlNS /////////////////////////////////////////////////////////////////////////// @@ -350,6 +361,8 @@ public class GDBRunControl_7_0_NS extends AbstractDsfService implements IMIRunCo protected Map<IMIExecutionDMContext, MIThreadRunState> fThreadRunStates = new HashMap<IMIExecutionDMContext, MIThreadRunState>(); private RunToLineActiveOperation fRunToLineActiveOperation = null; + + private StepIntoSelectionActiveOperation fStepInToSelectionActiveOperation = null; /** @since 4.0 */ protected RunToLineActiveOperation getRunToLineActiveOperation() { @@ -404,7 +417,8 @@ public class GDBRunControl_7_0_NS extends AbstractDsfService implements IMIRunCo register(new String[]{ IRunControl.class.getName(), IRunControl2.class.getName(), IMIRunControl.class.getName(), - IMultiRunControl.class.getName() }, + IMultiRunControl.class.getName(), + IRunControl3.class.getName()}, new Hashtable<String,String>()); fConnection = getServicesTracker().getService(ICommandControlService.class); fCommandFactory = getServicesTracker().getService(IMICommandControl.class).getCommandFactory(); @@ -712,6 +726,10 @@ public class GDBRunControl_7_0_NS extends AbstractDsfService implements IMIRunCo @Override public void step(IExecutionDMContext context, StepType stepType, final RequestMonitor rm) { + step(context, stepType, true, rm); + } + + private void step(IExecutionDMContext context, StepType stepType, boolean checkCanResume, final RequestMonitor rm) { assert context != null; @@ -722,9 +740,8 @@ public class GDBRunControl_7_0_NS extends AbstractDsfService implements IMIRunCo return; } - if (!doCanResume(dmc)) { - rm.done(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_STATE, - "Cannot resume context", null)); //$NON-NLS-1$ + if (checkCanResume && !doCanResume(dmc)) { + rm.done(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_STATE, "Cannot resume context", null)); //$NON-NLS-1$ return; } @@ -839,6 +856,7 @@ public class GDBRunControl_7_0_NS extends AbstractDsfService implements IMIRunCo fConnection.queueCommand(fCommandFactory.createMIBreakDelete(bpDmc, new int[] {bpId}), new DataRequestMonitor<MIInfo>(getExecutor(), null)); fRunToLineActiveOperation = null; + fStepInToSelectionActiveOperation = null; super.handleFailure(); } @@ -849,9 +867,87 @@ public class GDBRunControl_7_0_NS extends AbstractDsfService implements IMIRunCo } // ------------------------------------------------------------------------ - // Resume at location + // Step into Selection // ------------------------------------------------------------------------ + private void stepIntoSelection(final IExecutionDMContext context, final int baseLine, final String baseLineLocation, final boolean skipBreakpoints, final IFunctionDeclaration targetFunction, + final RequestMonitor rm) { + + assert context != null; + + final IMIExecutionDMContext dmc = DMContexts.getAncestorOfType(context, IMIExecutionDMContext.class); + if (dmc == null) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Given context: " + context + " is not an MI execution context.", null)); //$NON-NLS-1$ //$NON-NLS-2$ + rm.done(); + return; + } + + if (!doCanResume(dmc)) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_STATE, "Cannot resume context", null)); //$NON-NLS-1$ + rm.done(); + return; + } + + MIThreadRunState threadState = fThreadRunStates.get(dmc); + if (threadState == null) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_STATE, "Given context: " + context + " can't be found.", null)); //$NON-NLS-1$ //$NON-NLS-2$ + rm.done(); + return; + } + + if (threadState.fLatestEvent == null || !(threadState.fLatestEvent instanceof SuspendedEvent)) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_STATE, "Given context: " + context + " invalid suspended event.", null)); //$NON-NLS-1$ //$NON-NLS-2$ + rm.done(); + return; + } + + SuspendedEvent suspendedEvent = (SuspendedEvent) threadState.fLatestEvent; + final MIFrame currentFrame = suspendedEvent.getMIEvent().getFrame(); + if (currentFrame == null) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_STATE, "Given event: " + suspendedEvent + " invalid frame in suspended event.", null)); //$NON-NLS-1$ //$NON-NLS-2$ + rm.done(); + return; + } + + getStackDepth(dmc, new DataRequestMonitor<Integer>(getExecutor(), rm) { + @Override + public void handleSuccess() { + if (getData() != null) { + final int framesSize = getData().intValue(); + + // make sure the operation is removed upon + // failure detection + final RequestMonitor rms = new RequestMonitor(getExecutor(), rm) { + @Override + protected void handleFailure() { + fStepInToSelectionActiveOperation = null; + super.handleFailure(); + } + }; + if ((currentFrame.getFile() + ":" + currentFrame.getLine()).endsWith(baseLineLocation)) { //$NON-NLS-1$ + // Save the step into selection information + fStepInToSelectionActiveOperation = new StepIntoSelectionActiveOperation(dmc, baseLine, targetFunction, framesSize, + currentFrame); + // Ready to step into a function selected + // within a current line + step(dmc, StepType.STEP_INTO, rms); + } else { + // Save the step into selection information + fStepInToSelectionActiveOperation = new StepIntoSelectionActiveOperation(dmc, baseLine, targetFunction, framesSize, null); + // Pointing to a line different than the current line + // Needs to RunToLine before stepping to the selection + runToLocation(dmc, baseLineLocation, skipBreakpoints, rms); + } + } else { + rm.done(); + } + } + }); + } + + // ------------------------------------------------------------------------ + // Resume at location + // ------------------------------------------------------------------------ private void resumeAtLocation(IExecutionDMContext context, String location, RequestMonitor rm) { assert context != null; @@ -958,6 +1054,7 @@ public class GDBRunControl_7_0_NS extends AbstractDsfService implements IMIRunCo threadState.fStateChangeReason = reason; threadState.fStateChangeDetails = null; // we have no details of interest for a resume threadState.fStepping = isStepping; + threadState.fLatestEvent = event; } private void updateThreadState(IMIExecutionDMContext context, SuspendedEvent event) { @@ -971,7 +1068,8 @@ public class GDBRunControl_7_0_NS extends AbstractDsfService implements IMIRunCo threadState.fResumePending = false; threadState.fStepping = false; threadState.fStateChangeReason = reason; - threadState.fStateChangeDetails = event.getDetails(); + threadState.fStateChangeDetails = event.getDetails(); + threadState.fLatestEvent = event; } /* ****************************************************************************** @@ -1439,89 +1537,225 @@ public class GDBRunControl_7_0_NS extends AbstractDsfService implements IMIRunCo */ @DsfServiceEventHandler public void eventDispatched(final MIStoppedEvent e) { - if (fRunToLineActiveOperation != null) { - // First check if it is the right thread that stopped - IMIExecutionDMContext threadDmc = DMContexts.getAncestorOfType(e.getDMContext(), IMIExecutionDMContext.class); - if (fRunToLineActiveOperation.getThreadContext().equals(threadDmc)) { - int bpId = 0; - if (e instanceof MIBreakpointHitEvent) { - bpId = ((MIBreakpointHitEvent)e).getNumber(); - } - String fileLocation = e.getFrame().getFile() + ":" + e.getFrame().getLine(); //$NON-NLS-1$ - String addrLocation = e.getFrame().getAddress(); - // Here we check three different things to see if we are stopped at the right place - // 1- The actual location in the file. But this does not work for breakpoints that - // were set on non-executable lines - // 2- The address where the breakpoint was set. But this does not work for breakpoints - // that have multiple addresses (GDB returns <MULTIPLE>.) I think that is for multi-process - // 3- The breakpoint id that was hit. But this does not work if another breakpoint - // was also set on the same line because GDB may return that breakpoint as being hit. - // - // So this works for the large majority of cases. The case that won't work is when the user - // does a runToLine to a line that is non-executable AND has another breakpoint AND - // has multiple addresses for the breakpoint. I'm mean, come on! - if (fileLocation.equals(fRunToLineActiveOperation.getFileLocation()) || - addrLocation.equals(fRunToLineActiveOperation.getAddrLocation()) || - bpId == fRunToLineActiveOperation.getBreakointId()) { - // We stopped at the right place. All is well. - fRunToLineActiveOperation = null; - } else { - // The right thread stopped but not at the right place yet - if (fRunToLineActiveOperation.shouldSkipBreakpoints() && e instanceof MIBreakpointHitEvent) { - fConnection.queueCommand( - fCommandFactory.createMIExecContinue(fRunToLineActiveOperation.getThreadContext()), - new DataRequestMonitor<MIInfo>(getExecutor(), null)); - - // Don't send the stop event since we are resuming again. - return; - } else { - // Stopped for any other reasons. Just remove our temporary one - // since we don't want it to hit later - // - // Note that in Non-stop, we don't cancel a run-to-line when a new - // breakpoint is inserted. This is because the new breakpoint could - // be for another thread altogether and should not affect the current thread. - IBreakpointsTargetDMContext bpDmc = DMContexts.getAncestorOfType(fRunToLineActiveOperation.getThreadContext(), - IBreakpointsTargetDMContext.class); - - fConnection.queueCommand(fCommandFactory.createMIBreakDelete(bpDmc, new int[] {fRunToLineActiveOperation.getBreakointId()}), - new DataRequestMonitor<MIInfo>(getExecutor(), null)); - fRunToLineActiveOperation = null; - } - } - } - } - + if (processRunToLineStoppedEvent(e)) { + // If RunToLine is not completed + return; + } + + if (!processStepIntoSelection(e)) { + //Step into Selection is not in progress + broadcastStop(e); + } + } + + private void broadcastStop(final MIStoppedEvent e) { IMIExecutionDMContext threadDmc = DMContexts.getAncestorOfType(e.getDMContext(), IMIExecutionDMContext.class); - if (e instanceof MISignalEvent && fDisableNextSignalEventDmcSet.remove(threadDmc)) { + if (e instanceof MISignalEvent && fDisableNextSignalEventDmcSet.remove(threadDmc)) { fSilencedSignalEventMap.put(threadDmc, e); - // Don't broadcast the stopped event return; } + + IDMEvent<?> event = null; + MIBreakpointDMContext bp = null; + if (e instanceof MIBreakpointHitEvent) { + int bpId = ((MIBreakpointHitEvent) e).getNumber(); + IBreakpointsTargetDMContext bpsTarget = DMContexts.getAncestorOfType(e.getDMContext(), IBreakpointsTargetDMContext.class); + if (bpsTarget != null && bpId >= 0) { + bp = new MIBreakpointDMContext(getSession().getId(), new IDMContext[] { bpsTarget }, bpId); + event = new BreakpointHitEvent(e.getDMContext(), (MIBreakpointHitEvent) e, bp); + } + } + if (event == null) { + event = new SuspendedEvent(e.getDMContext(), e); + } + + getSession().dispatchEvent(event, getProperties()); + } + + private boolean processStepIntoSelection(final MIStoppedEvent e) { + if (fStepInToSelectionActiveOperation == null) { + return false; + } - IDMEvent<?> event = null; - MIBreakpointDMContext bp = null; - if (e instanceof MIBreakpointHitEvent) { - int bpId = ((MIBreakpointHitEvent)e).getNumber(); - IBreakpointsTargetDMContext bpsTarget = DMContexts.getAncestorOfType(e.getDMContext(), IBreakpointsTargetDMContext.class); - if (bpsTarget != null && bpId >= 0) { - bp = new MIBreakpointDMContext(getSession().getId(), new IDMContext[] {bpsTarget}, bpId); - event = new BreakpointHitEvent(e.getDMContext(), (MIBreakpointHitEvent)e, bp); - } - } - if (event == null) { - event = new SuspendedEvent(e.getDMContext(), e); - } - - getSession().dispatchEvent(event, getProperties()); + // First check if it is the right thread that stopped + final IMIExecutionDMContext threadDmc = DMContexts.getAncestorOfType(e.getDMContext(), IMIExecutionDMContext.class); + if (fStepInToSelectionActiveOperation.getThreadContext().equals(threadDmc)) { + final MIFrame frame = e.getFrame(); + + assert(fRunToLineActiveOperation == null); + + if (fStepInToSelectionActiveOperation.getRunToLineFrame() == null) { + assert(fStepInToSelectionActiveOperation.getLine() == frame.getLine()); + // Shall now be at the runToline location + fStepInToSelectionActiveOperation.setRunToLineFrame(frame); + } + + // Step - Not at the right place just yet + // Initiate an async call chain parent + getStackDepth(threadDmc, new DataRequestMonitor<Integer>(getExecutor(), null) { + private int originalStackDepth = fStepInToSelectionActiveOperation.getOriginalStackDepth(); + + @Override + protected void handleSuccess() { + int frameDepth = getStackDepth(); + + if (frameDepth > originalStackDepth) { + //shall be true as this is using stepinto step type vs instruction stepinto + assert(frameDepth == originalStackDepth + 1); + + // Check for a match + if (StepIntoSelectionUtils.sameSignature(frame, fStepInToSelectionActiveOperation)) { + // Hit !! + stopStepIntoSelection(e); + return; + } + + // Located deeper in the stack, Shall continue step / search + // Step return + continueStepping(e, StepType.STEP_RETURN); + } else if (frameDepth == originalStackDepth) { + // Continue step / search as long as + // this is the starting base line for the search + String currentLocation = frame.getFile() + ":" + frame.getLine(); //$NON-NLS-1$ + String searchLineLocation = fStepInToSelectionActiveOperation.getFileLocation(); + if (currentLocation.equals(searchLineLocation)) { + continueStepping(e, StepType.STEP_INTO); + } else { + // We have moved to a line + // different from the base + // search line i.e. missed the + // target function !! + StepIntoSelectionUtils.missedSelectedTarget(fStepInToSelectionActiveOperation); + stopStepIntoSelection(e); + } + } else { + // missed the target point + StepIntoSelectionUtils.missedSelectedTarget(fStepInToSelectionActiveOperation); + } + } + + @Override + protected void handleFailure() { + // log error + if (getStatus() != null) { + GdbPlugin.getDefault().getLog().log(getStatus()); + } + + stopStepIntoSelection(e); + } + + private int getStackDepth() { + Integer stackDepth = null; + if (isSuccess() && getData() != null) { + stackDepth = getData(); + // This is the base frame, the original stack depth shall be updated + if (frame == fStepInToSelectionActiveOperation.getRunToLineFrame()) { + fStepInToSelectionActiveOperation.setOriginalStackDepth(stackDepth); + originalStackDepth = stackDepth; + } + } + + if (stackDepth == null) { + // Unsuccessful resolution of stack depth, default to same stack depth to detect a change of line within the original frame + return fStepInToSelectionActiveOperation.getOriginalStackDepth(); + } + + return stackDepth.intValue(); + } + }); + + //Processing step into selection + return true; + } + + //The thread related to this event is outside the scope of the step into selection context + return false; } + private void stopStepIntoSelection(final MIStoppedEvent e) { + fStepInToSelectionActiveOperation = null; + // Need to broadcast the stop + broadcastStop(e); + } - /** - * @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. - */ + private void continueStepping(final MIStoppedEvent event, StepType steptype) { + step(fStepInToSelectionActiveOperation.getThreadContext(), steptype, false, new RequestMonitor(getExecutor(), null) { + @Override + protected void handleFailure() { + // log error + if (getStatus() != null) { + GdbPlugin.getDefault().getLog().log(getStatus()); + } + + stopStepIntoSelection(event); + } + }); + } + + private boolean processRunToLineStoppedEvent(final MIStoppedEvent e) { + if (fRunToLineActiveOperation == null) { + return false; + } + + // First check if it is the right thread that stopped + IMIExecutionDMContext threadDmc = DMContexts.getAncestorOfType(e.getDMContext(), IMIExecutionDMContext.class); + if (fRunToLineActiveOperation.getThreadContext().equals(threadDmc)) { + int bpId = 0; + if (e instanceof MIBreakpointHitEvent) { + bpId = ((MIBreakpointHitEvent) e).getNumber(); + } + + String fileLocation = e.getFrame().getFile() + ":" + e.getFrame().getLine(); //$NON-NLS-1$ + String addrLocation = e.getFrame().getAddress(); + + // Here we check three different things to see if we are stopped at the right place + // 1- The actual location in the file. But this does not work for breakpoints that + // were set on non-executable lines + // 2- The address where the breakpoint was set. But this does not work for breakpoints + // that have multiple addresses (GDB returns <MULTIPLE>.) I think that is for multi-process + // 3- The breakpoint id that was hit. But this does not work if another breakpoint + // was also set on the same line because GDB may return that breakpoint as being hit. + // + // So this works for the large majority of cases. The case that won't work is when the user + // does a runToLine to a line that is non-executable AND has another breakpoint AND + // has multiple addresses for the breakpoint. I'm mean, come on! + if (fileLocation.equals(fRunToLineActiveOperation.getFileLocation()) || addrLocation.equals(fRunToLineActiveOperation.getAddrLocation()) + || bpId == fRunToLineActiveOperation.getBreakointId()) { + // We stopped at the right place. All is well. + // Run to line completed + fRunToLineActiveOperation = null; + } else { + // The right thread stopped but not at the right place yet + if (fRunToLineActiveOperation.shouldSkipBreakpoints() && e instanceof MIBreakpointHitEvent) { + fConnection.queueCommand(fCommandFactory.createMIExecContinue(fRunToLineActiveOperation.getThreadContext()), new DataRequestMonitor<MIInfo>(getExecutor(), null)); + + // Continue i.e. Don't send the stop event since we are + // resuming again. + return true; + } else { + // Stopped for any other reasons. Just remove our temporary one + // since we don't want it to hit later + // + // Note that in Non-stop, we don't cancel a run-to-line when a new + // breakpoint is inserted. This is because the new breakpoint could + // be for another thread altogether and should not affect the current thread. + IBreakpointsTargetDMContext bpDmc = DMContexts.getAncestorOfType(fRunToLineActiveOperation.getThreadContext(), IBreakpointsTargetDMContext.class); + + fConnection.queueCommand(fCommandFactory.createMIBreakDelete(bpDmc, new int[] { fRunToLineActiveOperation.getBreakointId() }), new DataRequestMonitor<MIInfo>(getExecutor(), null)); + fRunToLineActiveOperation = null; + fStepInToSelectionActiveOperation = null; + } + } + } + + return 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(final MIThreadCreatedEvent e) { IContainerDMContext containerDmc = e.getDMContext(); @@ -1620,6 +1854,7 @@ public class GDBRunControl_7_0_NS extends AbstractDsfService implements IMIRunCo new DataRequestMonitor<MIInfo>(getExecutor(), null)); fRunToLineActiveOperation = null; } + fStepInToSelectionActiveOperation = null; } /** @@ -2209,4 +2444,46 @@ public class GDBRunControl_7_0_NS extends AbstractDsfService implements IMIRunCo } return execDmcForOperationList; } + + /** + * @since 4.2 + */ + @Override + public void canStepIntoSelection(IExecutionDMContext context, String sourceFile, int lineNumber, IFunctionDeclaration selectedFunction, DataRequestMonitor<Boolean> rm) { + canStep(context, StepType.STEP_INTO, rm); + } + + /** + * @since 4.2 + */ + @Override + public void stepIntoSelection(final IExecutionDMContext context, String sourceFile, final int lineNumber, final boolean skipBreakpoints, final IFunctionDeclaration selectedFunction, final RequestMonitor rm) { + determineDebuggerPath(context, sourceFile, new ImmediateDataRequestMonitor<String>(rm) { + @Override + protected void handleSuccess() { + stepIntoSelection(context, lineNumber, getData() + ":" + Integer.toString(lineNumber), skipBreakpoints, selectedFunction, rm); //$NON-NLS-1$ + } + }); + } + + /** + * Help method used when the stopped event has not been broadcasted e.g. in the middle of step into selection + * + * @param dmc + * @param rm + */ + private void getStackDepth(final IMIExecutionDMContext dmc, final DataRequestMonitor<Integer> rm) { + if (dmc != null) { + fConnection.queueCommand(fCommandFactory.createMIStackInfoDepth(dmc), new DataRequestMonitor<MIStackInfoDepthInfo>(getExecutor(), rm) { + @Override + protected void handleSuccess() { + rm.setData(getData().getDepth()); + rm.done(); + } + }); + } else { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, "Invalid context", null)); //$NON-NLS-1$ + rm.done(); + } + } } diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/MIRunControl.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/MIRunControl.java index de72f05ae9b..e5cc1412f58 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/MIRunControl.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/MIRunControl.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2006, 2012 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 @@ -11,6 +11,7 @@ * Vladimir Prus (Mentor Graphics) - Add proper stop reason for step return (Bug 362274) * Indel AG - [369622] fixed moveToLine using MinGW * Marc Khouzam (Ericsson) - Make each thread an IDisassemblyDMContext (bug 352748) + * Alvaro Sanchez-Leon (Ericsson AB) - Support for Step into selection (bug 244865) *******************************************************************************/ package org.eclipse.cdt.dsf.mi.service; @@ -19,6 +20,7 @@ import java.util.LinkedList; import java.util.Map; import org.eclipse.cdt.core.IAddress; +import org.eclipse.cdt.core.model.IFunctionDeclaration; import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants; import org.eclipse.cdt.dsf.concurrent.ImmediateDataRequestMonitor; @@ -41,6 +43,7 @@ import org.eclipse.cdt.dsf.debug.service.IBreakpointsExtension.IBreakpointHitDME import org.eclipse.cdt.dsf.debug.service.ICachingService; import org.eclipse.cdt.dsf.debug.service.IDisassembly.IDisassemblyDMContext; import org.eclipse.cdt.dsf.debug.service.IProcesses; +import org.eclipse.cdt.dsf.debug.service.IRunControl3; import org.eclipse.cdt.dsf.debug.service.ISourceLookup; import org.eclipse.cdt.dsf.debug.service.ISourceLookup.ISourceLookupDMContext; import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMContext; @@ -96,7 +99,7 @@ import org.osgi.framework.BundleContext; * state. * @since 3.0 */ -public class MIRunControl extends AbstractDsfService implements IMIRunControl, ICachingService +public class MIRunControl extends AbstractDsfService implements IMIRunControl, ICachingService, IRunControl3 { private static class MIExecutionDMC extends AbstractDMContext implements IMIExecutionDMContext, IDisassemblyDMContext { @@ -375,6 +378,10 @@ public class MIRunControl extends AbstractDsfService implements IMIRunControl, I private boolean fResumePending = false; private boolean fStepping = false; private boolean fTerminated = false; + /** + * @since 4.2 + */ + protected RunControlEvent<IExecutionDMContext, ?> fLatestEvent = null; /** @@ -411,7 +418,7 @@ public class MIRunControl extends AbstractDsfService implements IMIRunControl, I super(session); } - @Override + @Override public void initialize(final RequestMonitor rm) { super.initialize( new ImmediateRequestMonitor(rm) { @@ -593,6 +600,8 @@ public class MIRunControl extends AbstractDsfService implements IMIRunControl, I fStateChangeReason = e.getReason(); fStateChangeDetails = null; // we have no details of interest for a resume fMICommandCache.setContextAvailable(e.getDMContext(), false); + fLatestEvent = e; + //fStateChangeTriggeringContext = e.getTriggeringContext(); if (e.getReason().equals(StateChangeReason.STEP)) { fStepping = true; @@ -615,7 +624,8 @@ public class MIRunControl extends AbstractDsfService implements IMIRunControl, I ? e.getTriggeringContexts()[0] : null; fSuspended = true; fStepping = false; - + fLatestEvent = e; + fResumePending = false; } @@ -780,7 +790,14 @@ public class MIRunControl extends AbstractDsfService implements IMIRunControl, I } @Override - public void step(final IExecutionDMContext context, StepType stepType, final RequestMonitor rm) { + public void step(IExecutionDMContext context, StepType stepType, final RequestMonitor rm) { + step(context, stepType, true, rm); + } + + /** + * @since 4.2 + */ + protected void step(final IExecutionDMContext context, StepType stepType, boolean checkCanResume, final RequestMonitor rm) { assert context != null; IMIExecutionDMContext dmc = DMContexts.getAncestorOfType(context, IMIExecutionDMContext.class); @@ -790,7 +807,7 @@ public class MIRunControl extends AbstractDsfService implements IMIRunControl, I return; } - if (!doCanResume(context)) { + if (checkCanResume && !doCanResume(context)) { rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_STATE, "Cannot resume context", null)); //$NON-NLS-1$ rm.done(); return; @@ -1594,8 +1611,9 @@ public class MIRunControl extends AbstractDsfService implements IMIRunControl, I * @param dmc A context that can be used to obtain the sourcelookup context. * @param hostPath The path of the file on the host, which must be converted. * @param rm The result of the conversion. + * @since 4.2 */ - private void determineDebuggerPath(IDMContext dmc, String hostPath, final DataRequestMonitor<String> rm) + protected void determineDebuggerPath(IDMContext dmc, String hostPath, final DataRequestMonitor<String> rm) { ISourceLookup sourceLookup = getServicesTracker().getService(ISourceLookup.class); ISourceLookupDMContext srcDmc = DMContexts.getAncestorOfType(dmc, ISourceLookupDMContext.class); @@ -1615,4 +1633,22 @@ public class MIRunControl extends AbstractDsfService implements IMIRunControl, I } }); } + + /** + * @since 4.2 + */ + @Override + public void canStepIntoSelection(IExecutionDMContext context, String sourceFile, int lineNumber, IFunctionDeclaration selectedFunction, DataRequestMonitor<Boolean> rm) { + rm.done(false); + } + + /** + * @since 4.2 + */ + @Override + public void stepIntoSelection(IExecutionDMContext context, String sourceFile, int lineNumber, boolean skipBreakpoints, IFunctionDeclaration selectedFunction, RequestMonitor rm) { + IStatus status = new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, DebugException.REQUEST_FAILED, "Step Into Selection not supported", null); //$NON-NLS-1$ + rm.done(status); + } + } diff --git a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/data/launch/src/Artifact.cc b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/data/launch/src/Artifact.cc new file mode 100644 index 00000000000..7e98cbf6be9 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/data/launch/src/Artifact.cc @@ -0,0 +1,49 @@ + +using namespace std; + +#include<string> +//#include<> + +//The 'Component' node +class Artifact { +public: + Artifact(string name) { + fName = name; + fParent = NULL; + } + + //Exercising Polymorphysm + virtual void print() = 0; + virtual string toString() = 0; + virtual void print(char& padc) = 0; + virtual string toString(char& padc) = 0; + + string getName() { + return fName; + } + + string getLocation() { + return fPath + "/" + fName; + } + + virtual void setParent(Artifact &parent) { + fPath = parent.getLocation(); + fParent = &parent; + } + + void deleteParent() { + fPath = ""; + fParent = NULL; + } + + virtual ~Artifact() { + } + +protected: + string fName; + string fPath; + Artifact* fParent; + +private: + Artifact(); +}; diff --git a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/data/launch/src/Composite.cc b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/data/launch/src/Composite.cc new file mode 100644 index 00000000000..dae86028134 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/data/launch/src/Composite.cc @@ -0,0 +1,210 @@ +/* + * Composite Pattern + */ +#include<iostream> +#include<string> +#include<vector> +#include "Leaf.cc" + +using namespace std; + +//The 'Composite' node +class CompositeNode: public Artifact { +public: + CompositeNode(string name) : + Artifact(name) { + } + + void Add(Artifact* child) { + child->setParent(*this); + fChildren.push_back(child); + } + + void setParent(Artifact &parent) { + fPath = parent.getLocation(); + fParent = &parent; + + //Refresh the parent information path to all children + vector<Artifact*>::iterator it = fChildren.begin(); + while (it != fChildren.end()) { + Artifact* child = *it; + child->setParent(*this); + ++it; + } + } + + void Remove(Artifact* child) { + child->deleteParent(); + vector<Artifact*>::iterator it = fChildren.begin(); + while (it != fChildren.end()) { + if (*it == child) { + delete child; + fChildren.erase(it); + break; + } + ++it; + } + + } + + void print() { + cout << getLocation() << endl; + + vector<Artifact*>::iterator it = fChildren.begin(); + while (it != fChildren.end()) { + (*it)->print(); + ++it; + } + } + + void print(char& cpad) { + string padding(fPath.length(), cpad); + cout << padding << "+ " << fName << endl; + + vector<Artifact*>::iterator it = fChildren.begin(); + while (it != fChildren.end()) { + (*it)->print(cpad); + ++it; + } + } + + string toString() { + string strAccumulator(getLocation() + "\n"); + + vector<Artifact*>::iterator it = fChildren.begin(); + while (it != fChildren.end()) { + strAccumulator.append((*it)->toString()); + ++it; + } + + return strAccumulator; + } + + string toString(char& cpad) { + string strAccumulation(fPath.length(), cpad); + strAccumulation.append("+ " + fName + "\n"); + + vector<Artifact*>::iterator it = fChildren.begin(); + while (it != fChildren.end()) { + strAccumulation.append((*it)->toString(cpad)); + ++it; + } + + return strAccumulation; + } + + virtual int getArtifactsSize() { + return fChildren.size(); + } + + virtual Artifact* getArtifact(int index) { + if (index < fChildren.size()) { + return fChildren.at(index); + } + + else + return 0; + } + + virtual Artifact* getArtifact(string description) { + vector<Artifact*>::iterator it = fChildren.begin(); + while (it != fChildren.end()) { + if ((*it)->getName().compare(description)) { + return *it; + } + ++it; + } + + return 0; + } + + virtual ~CompositeNode() { + while (!fChildren.empty()) { + vector<Artifact*>::iterator it = fChildren.begin(); + delete *it; + fChildren.erase(it); + } + } + +private: + CompositeNode(); + vector<Artifact*> fChildren; +}; + +//The Main method +int main() { + //Create a tree root + CompositeNode* root = new CompositeNode("Dogs"); + + //Create composite nodes + CompositeNode* comp = new CompositeNode("Companion"); + comp->Add(new LeafNode("Puddle")); + comp->Add(new LeafNode("Bichon")); + + CompositeNode* sport = new CompositeNode("Guardian"); + sport->Add(new LeafNode("Boxer")); + sport->Add(new LeafNode("Rottweiler")); + sport->Add(new LeafNode("Mastiff")); + + //Create a Branch + CompositeNode* gun = new CompositeNode("Gun"); + gun->Add(new LeafNode("Cocker")); + gun->Add(new LeafNode("Pointer")); + gun->Add(new LeafNode("Golden Retriever")); + + CompositeNode* herd = new CompositeNode("Herding"); + herd->Add(new LeafNode("Cattle dog")); + herd->Add(new LeafNode("Sheepdog")); + + CompositeNode* north = new CompositeNode("Northern"); + north->Add(new LeafNode("Akita")); + north->Add(new LeafNode("Chow Chow")); + + CompositeNode* hound = new CompositeNode("Hound"); + hound->Add(new LeafNode("Basset Hound")); + hound->Add(new LeafNode("Beagle")); + + CompositeNode* terrier = new CompositeNode("Terrier"); + terrier->Add(new LeafNode("Bull Terrier")); + terrier->Add(new LeafNode("Border Terrier")); + + //Create some leaf nodes + LeafNode* pe1 = new LeafNode("German Shepperd"); + LeafNode* pe2 = new LeafNode("Great Dane"); + + //Add nodes to start from the same root + root->Add(comp); + root->Add(sport); + root->Add(gun); + root->Add(herd); + root->Add(north); + root->Add(hound); + root->Add(terrier); + //Add leaf nodes to root + root->Add(pe1); + root->Add(pe2); + + char cpad = '-'; + char cpad2 = '_'; + //Test stub + toString variants + if (root->getArtifactsSize() > 0 + && (root->getArtifact(0) != 0 && (root->getArtifact("Bichon") != 0))) { + string sout = root->getArtifact(0)->toString() + "\n" + root->getArtifact(1)->toString(cpad2); + cout << sout << endl; + } + + //Test Remove primitive elements + root->Remove(pe1); + root->Remove(pe2); + + //Test Print variants + root->getArtifact(2)->print(); root->getArtifact(3)->print(cpad); + + //Test toString all + cout << "\n\nAll Tree\n" + root->toString(cpad); + + //delete the allocated memory + delete root; + + return 0; +} diff --git a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/data/launch/src/Leaf.cc b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/data/launch/src/Leaf.cc new file mode 100644 index 00000000000..bc53da4ef34 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/data/launch/src/Leaf.cc @@ -0,0 +1,38 @@ +#include<iostream> +#include<string> +#include "Artifact.cc" + + +//The 'Leaf' class +class LeafNode: public Artifact { +public: + LeafNode(string name) : + Artifact(name) { + } + + void print() { + cout << getLocation() << endl; + } + + void print(char& cpad) { + string padding(fPath.length(), cpad); + cout << padding << " " << fName << endl; + } + + + string toString() { + return getLocation() + "\n"; + } + + string toString(char& cpad) { + string padding(fPath.length(), cpad); + string rstr = padding + " " + fName + "\n"; + return rstr; + } + + virtual ~LeafNode() { + } + +private: + LeafNode(); +}; diff --git a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/framework/ServiceEventWaitor.java b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/framework/ServiceEventWaitor.java index be375f9c9c1..b64c80f21f8 100644 --- a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/framework/ServiceEventWaitor.java +++ b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/framework/ServiceEventWaitor.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2007, 2012 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 @@ -8,6 +8,7 @@ * Contributors: * Ericsson - Initial Implementation * Marc Khouzam (Ericsson) - Add support to receive multiple events + * Alvaro Sanchez-Leon (Ericsson) - Add filter out and wait for a given type of event *******************************************************************************/ package org.eclipse.cdt.tests.dsf.gdb.framework; @@ -94,6 +95,30 @@ public class ServiceEventWaitor<V> { } } + /** + * Will wait and discard events that are not either of the specified type or a subtype + * It will stop and return the first one found or exit after the specified timeout + * + * @param type - The parent type of an acceptable event + * @param timeout the maximum time to wait in milliseconds to wait for a specified event + */ + @SuppressWarnings("unchecked") + public synchronized <T extends V> T waitForEvent(Class<T> type, int timeout) throws Exception { + long startMs = System.currentTimeMillis(); + //The Specified Event received or Timeout exception will exit the loop + while (true) { + int timeRemaining = (int) (timeout - (System.currentTimeMillis() - startMs)); + if (timeRemaining > 0) { + V sevent = waitForEvent(timeRemaining); + if (type.isAssignableFrom(sevent.getClass())) { + return (T) sevent; + } + } else { + throw new Exception("Timed out waiting for ServiceEvent: " + type.getName()); + } + } + } + /* * Block until 'timeout' or the expected event occurs. The expected event is * specified at construction time. @@ -163,7 +188,10 @@ public class ServiceEventWaitor<V> { } } - return fEventQueue.remove(0); + V vevent = fEventQueue.remove(0); + + + return vevent; } /* @@ -177,6 +205,8 @@ public class ServiceEventWaitor<V> { fEventQueue.add(event); notifyAll(); } + } else { + System.out.println("NOT QUEUEING: SevericeEventWaitor: Class: " + fEventTypeClass.getName() + " is NOT assignable from event class: " + event.getClass()); } } } diff --git a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/AllTests.java b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/AllTests.java index 5b00c8877b2..2497c8243a8 100644 --- a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/AllTests.java +++ b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/AllTests.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2009, 2011 Ericsson and others. + * Copyright (c) 2009, 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.junit.runners.Suite; PostMortemCoreTest.class, CommandTimeoutTest.class, Suite_Sessionless_Tests.class, + StepIntoSelectionTest.class, /* Add your suite class here */ }) diff --git a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/StepIntoSelectionTest.java b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/StepIntoSelectionTest.java new file mode 100644 index 00000000000..92a5cf2a1df --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/StepIntoSelectionTest.java @@ -0,0 +1,395 @@ +/******************************************************************************* + * 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: + * Alvaro Sanchez-Leon (Ericsson AB) - Support for Step into selection (bug 244865) + *******************************************************************************/ +package org.eclipse.cdt.tests.dsf.gdb.tests; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import javax.naming.OperationNotSupportedException; + +import org.eclipse.cdt.core.model.IFunctionDeclaration; +import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.debug.service.IProcesses.IProcessDMContext; +import org.eclipse.cdt.dsf.debug.service.IProcesses.IThreadDMContext; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext; +import org.eclipse.cdt.dsf.debug.service.IRunControl3; +import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMData; +import org.eclipse.cdt.dsf.gdb.IGDBLaunchConfigurationConstants; +import org.eclipse.cdt.dsf.gdb.service.command.IGDBControl; +import org.eclipse.cdt.dsf.mi.service.IMIProcesses; +import org.eclipse.cdt.dsf.mi.service.MIProcesses; +import org.eclipse.cdt.dsf.mi.service.command.events.MILocationReachedEvent; +import org.eclipse.cdt.dsf.mi.service.command.events.MIStoppedEvent; +import org.eclipse.cdt.dsf.mi.service.command.output.MIFrame; +import org.eclipse.cdt.dsf.service.DsfServicesTracker; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.internal.core.model.FunctionDeclaration; +import org.eclipse.cdt.tests.dsf.gdb.framework.AsyncCompletionWaitor; +import org.eclipse.cdt.tests.dsf.gdb.framework.BackgroundRunner; +import org.eclipse.cdt.tests.dsf.gdb.framework.BaseTestCase; +import org.eclipse.cdt.tests.dsf.gdb.framework.ServiceEventWaitor; +import org.eclipse.cdt.tests.dsf.gdb.framework.SyncUtil; +import org.eclipse.cdt.tests.dsf.gdb.launching.TestsPlugin; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * Tests Non Stop GDB RunControl "Step into Selection feature" + * + */ +@SuppressWarnings("restriction") +@RunWith(BackgroundRunner.class) +public class StepIntoSelectionTest extends BaseTestCase { + + private DsfServicesTracker fServicesTracker; + + private IGDBControl fGDBCtrl; + private IRunControl3 fRunCtrl; + + private IContainerDMContext fContainerDmc; + private IExecutionDMContext fThreadExecDmc; + + /* + * Path to executable + */ + private static final String EXEC_PATH = "data/launch/bin/"; + + /* + * Name of the executable + */ + private static final String BIN_COMPOSITE = "Composite.exe"; + + // Composite Locations + private static final String SRC_COMPOSITE = "Composite.cc"; + private static final int COMPOSITE_GETARTIFACTSIZE_LINE_1 = 97; + private static final int COMPOSITE_GETARTIFACT_LINE_1 = 101; + private static final int COMPOSITE_MAIN_LINE_S5 = 89; + private static final int COMPOSITE_MAIN_LINE_M1 = 190; + private static final int COMPOSITE_MAIN_LINE_M2 = 191; + private static final int COMPOSITE_MAIN_LINE_L1 = 192; + private static final int COMPOSITE_MAIN_LINE_L2 = 197; + private static final int COMPOSITE_MAIN_LINE_L3 = 201; + private static final int COMPOSITE_MAIN_LINE_L4 = 204; + private static final int COMPOSITE_TOSTRING_LINE_1 = 72; + private static final int COMPOSITE_TOSTRING_C_LINE_1 = 84; + private static final String COMPOSITE_GETARTIFACTSIZE = "getArtifactsSize"; + private static final String COMPOSITE_GETARTIFACT = "getArtifact"; + private static final String COMPOSITE_TOSTRING = "toString"; + + // Artifact Locations + private static final String ARTIFACT_GETLOCATION = "getLocation"; + private static final int ARTIFACT_GETLOCATION_LINE_1 = 26; + + // Leaf Locations + private static final String SRC_LEAF = "Leaf.cc"; + private static final int LEAF_PRINT_LINE_1 = 14; + + //Target Functions + private final static FunctionDeclaration funcCompGetArtifactSize = new FunctionDeclaration(null, COMPOSITE_GETARTIFACTSIZE); + private final static FunctionDeclaration funcCompGetArtifact_i = new FunctionDeclaration(null, COMPOSITE_GETARTIFACT); + private final static FunctionDeclaration funcArtifactGetLocation = new FunctionDeclaration(null, ARTIFACT_GETLOCATION); + private final static FunctionDeclaration funcCompToString = new FunctionDeclaration(null, COMPOSITE_TOSTRING); + private final static FunctionDeclaration funcCompToString_c = new FunctionDeclaration(null, COMPOSITE_TOSTRING); + + static { + funcCompGetArtifact_i.setParameterTypes(new String[]{"int"}); + funcCompToString_c.setParameterTypes(new String[]{"Char&"}); + } + + class ResultContext { + MIStoppedEvent fEvent = null; + IExecutionDMContext fContext = null; + + public ResultContext(MIStoppedEvent event, IExecutionDMContext context) { + this.fEvent = event; + this.fContext = context; + } + + public MIStoppedEvent getEvent() { + return fEvent; + } + + public IExecutionDMContext getContext() { + return fContext; + } + } + + @Override + public void doBeforeTest() throws Exception { + super.doBeforeTest(); + + final DsfSession session = getGDBLaunch().getSession(); + Runnable runnable = new Runnable() { + @Override + public void run() { + fServicesTracker = new DsfServicesTracker(TestsPlugin.getBundleContext(), session.getId()); + fGDBCtrl = fServicesTracker.getService(IGDBControl.class); + + IMIProcesses procService = fServicesTracker.getService(IMIProcesses.class); + IProcessDMContext procDmc = procService.createProcessContext(fGDBCtrl.getContext(), MIProcesses.UNIQUE_GROUP_ID); + fContainerDmc = procService.createContainerContext(procDmc, MIProcesses.UNIQUE_GROUP_ID); + IThreadDMContext threadDmc = procService.createThreadContext(procDmc, "1"); + fThreadExecDmc = procService.createExecutionContext(fContainerDmc, threadDmc, "1"); + + fRunCtrl = fServicesTracker.getService(IRunControl3.class); + } + }; + session.getExecutor().submit(runnable).get(); + } + + @Override + public void doAfterTest() throws Exception { + super.doAfterTest(); + + fServicesTracker.dispose(); + } + + @Override + protected void setLaunchAttributes() { + super.setLaunchAttributes(); + setLaunchAttribute(ICDTLaunchConfigurationConstants.ATTR_PROGRAM_NAME, EXEC_PATH + BIN_COMPOSITE); + setLaunchAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_NON_STOP, true); + } + + private IExecutionDMContext gdbRunToStartLine(String sourceName, int targetLine, ServiceEventWaitor<MIStoppedEvent> waitor) throws Throwable { + // run gdb to the specified line an resolve the execution context where the MI signal events are being processed + SyncUtil.runToLine(fThreadExecDmc, sourceName, Integer.toString(targetLine), true); + MILocationReachedEvent locEvent = waitor.waitForEvent(MILocationReachedEvent.class, TestsPlugin.massageTimeout(500)); + return locEvent.getDMContext(); + } + + private MIStoppedEvent getLastEvent(ServiceEventWaitor<MIStoppedEvent> gdbStopListener) { + // Fetch the last stopped event as stepping into selection needs to step several times. + MIStoppedEvent event = null; + // Run until Timeout exception i.e. no more events in the queue + try { + while (true) { + // Wait or fetch the next stopped event in the queue + event = gdbStopListener.waitForEvent(MIStoppedEvent.class, TestsPlugin.massageTimeout(500)); + } + } catch (Exception e) { + assertTrue("Exception: " + e.getMessage(), e.getMessage().contains("Timed out")); + } + + return event; + } + + private void validateLocation(IExecutionDMContext exeContext, MIFrame frame, String funcName) throws Throwable { + // Validate that the frame received is at the specified location + assertTrue(frame.getFunction().endsWith(funcName)); + + // Validate that GDB is in sync at the specified location + IFrameDMData gdbFrame = SyncUtil.getFrameData(exeContext, 0); + assertTrue(gdbFrame.getFunction().endsWith(funcName)); + } + + private void checkGdbIsSuspended() throws Throwable { + final AsyncCompletionWaitor wait = new AsyncCompletionWaitor(); + final IContainerDMContext containerDmc = SyncUtil.getContainerContext(); + + // Execution shall be suspended + fRunCtrl.getExecutor().submit(new Runnable() { + @Override + public void run() { + wait.setReturnInfo(fRunCtrl.isSuspended(containerDmc)); + wait.waitFinished(); + } + }); + + wait.waitUntilDone(TestsPlugin.massageTimeout(5000)); + assertTrue("Target is running. It should have been suspended", (Boolean) wait.getReturnInfo()); + + wait.waitReset(); + } + + private void triggerRunToLine(final IExecutionDMContext exeContext, final String sourceName, final int targetLine) throws InterruptedException { + final AsyncCompletionWaitor wait = new AsyncCompletionWaitor(); + + fRunCtrl.getExecutor().submit(new Runnable() { + @Override + public void run() { + fRunCtrl.runToLine(exeContext, sourceName, targetLine, true, new RequestMonitor(fRunCtrl.getExecutor(), null) { + @Override + protected void handleCompleted() { + wait.waitFinished(getStatus()); + } + }); + } + }); + + wait.waitUntilDone(TestsPlugin.massageTimeout(10000)); + wait.waitReset(); + } + + private void triggerStepIntoSelection(final IExecutionDMContext exeContext, final String sourceName, final int targetLine, final IFunctionDeclaration function, final boolean skipBreakPoints) throws InterruptedException { + final AsyncCompletionWaitor wait = new AsyncCompletionWaitor(); + final OperationNotSupportedException[] exception = new OperationNotSupportedException[1]; + // Trigger Stepping into a specified 'function' on the current line + fRunCtrl.getExecutor().submit(new Runnable() { + @Override + public void run() { + fRunCtrl.stepIntoSelection(exeContext, sourceName, targetLine, skipBreakPoints, function, new RequestMonitor(fRunCtrl.getExecutor(), null) { + @Override + protected void handleCompleted() { + wait.waitFinished(getStatus()); + } + }); + } + }); + + wait.waitUntilDone(TestsPlugin.massageTimeout(10000)); + wait.waitReset(); + + if (exception[0] != null) { + fail("Step into selection failed: " + exception[0].getMessage()); + } + + } + + private ResultContext runToLine(IExecutionDMContext exeContext, String sourceName, int runToLine) throws Throwable { + DsfSession session = getGDBLaunch().getSession(); + + ServiceEventWaitor<MIStoppedEvent> gdbStopListener = new ServiceEventWaitor<MIStoppedEvent>(session, MIStoppedEvent.class); + + // Trigger Run to line + triggerRunToLine(exeContext, sourceName, runToLine); + + // Fetch the last stopped event as stepping into selection needs to step several times. + MIStoppedEvent event = gdbStopListener.waitForEvent(MIStoppedEvent.class, TestsPlugin.massageTimeout(500)); + + assertNotNull(event); + + // Validate that the last stopped frame received is at the specified location + MIFrame frame = event.getFrame(); + assertTrue(frame.getLine() == runToLine); + return new ResultContext(event, exeContext); + } + + private ResultContext stepIntoSelectionBase(String sourceName, int runToLine, IFunctionDeclaration targetFunction) throws Throwable { + return stepIntoSelectionBase(sourceName, runToLine, targetFunction, true, true, null); + } + + private ResultContext stepIntoSelectionBase(String sourceName, int runToLine, IFunctionDeclaration targetFunction, boolean validateLocation, boolean skipBreakPoints, IExecutionDMContext dmc) throws Throwable { + DsfSession session = getGDBLaunch().getSession(); + + ServiceEventWaitor<MIStoppedEvent> gdbStopListener = new ServiceEventWaitor<MIStoppedEvent>(session, MIStoppedEvent.class); + + final IExecutionDMContext exeContext; + if (dmc == null) { + exeContext = gdbRunToStartLine(SRC_COMPOSITE, COMPOSITE_MAIN_LINE_M1, gdbStopListener); + } else { + exeContext = dmc; + } + + // Run to an initial line an resolve the execution context where the MI signal events are being processed + assertNotNull(exeContext); + + // Trigger Stepping into a specified 'function' and several lines below the current one + triggerStepIntoSelection(exeContext, sourceName, runToLine, targetFunction, skipBreakPoints); + + // Fetch the last stopped event as stepping into selection needs to step several times. + MIStoppedEvent event = getLastEvent(gdbStopListener); + assertNotNull(event); + + // Validate that the last stopped frame received is at the specified location + MIFrame frame = event.getFrame(); + + if (validateLocation) { + validateLocation(exeContext, frame, targetFunction.getElementName()); + } + + checkGdbIsSuspended(); + + return new ResultContext(event, exeContext); + } + + @Test + public void stepIntoSelection() throws Throwable { + ResultContext result = stepIntoSelectionBase(SRC_COMPOSITE, COMPOSITE_MAIN_LINE_M1, funcCompGetArtifactSize); + int currentLine = result.getEvent().getFrame().getLine(); + assertTrue(currentLine == COMPOSITE_GETARTIFACTSIZE_LINE_1); + } + + @Test + public void stepIntoSelectionWithRunToLine() throws Throwable { + ResultContext result = stepIntoSelectionBase(SRC_COMPOSITE, COMPOSITE_MAIN_LINE_M2, funcCompGetArtifact_i); + int currentLine = result.getEvent().getFrame().getLine(); + assertTrue(currentLine == COMPOSITE_GETARTIFACT_LINE_1); + } + + @Test + public void withSelectedLineOnDifferentFile() throws Throwable { + ResultContext result = stepIntoSelectionBase(SRC_LEAF, LEAF_PRINT_LINE_1, funcArtifactGetLocation); + int currentLine = result.getEvent().getFrame().getLine(); + assertTrue(currentLine == ARTIFACT_GETLOCATION_LINE_1); + } + + /** + * A break point is found before reaching search line + * + * @throws Throwable + */ + @Test + public void doNotSkipBreakPoints() throws Throwable { + // insert a break point before the run to line + SyncUtil.addBreakpoint(SRC_COMPOSITE + ":" + COMPOSITE_MAIN_LINE_L2); + //trigger step into selection skip break points is set to false + ResultContext result = stepIntoSelectionBase(SRC_COMPOSITE, COMPOSITE_MAIN_LINE_L4, funcCompToString_c, false, false, null); + MIStoppedEvent event = result.getEvent(); + int currentLine = event.getFrame().getLine(); + //validate location, it shall not reach the step to selection line but the break point line instead. + assertTrue(currentLine == COMPOSITE_MAIN_LINE_L2); + //Make sure the step to selection operation is no longer active by triggering a second run to line before the step into selection line + result = runToLine(result.getContext(), SRC_COMPOSITE, COMPOSITE_MAIN_LINE_L3); + event = result.getEvent(); + currentLine = event.getFrame().getLine(); + //validate location, did not reached the step to selection line but the break point + assertTrue(currentLine == COMPOSITE_MAIN_LINE_L3); + } + + @Test + public void diffMethodByArgsNumber() throws Throwable { + ResultContext result = stepIntoSelectionBase(SRC_COMPOSITE, COMPOSITE_MAIN_LINE_L1, funcCompToString_c); + int currentLine = result.getEvent().getFrame().getLine(); + assertTrue(currentLine == COMPOSITE_TOSTRING_C_LINE_1); //first line of toString(char& c) + } + + @Test + public void diffMethodByArgsNumber2() throws Throwable { + ResultContext result = stepIntoSelectionBase(SRC_COMPOSITE, COMPOSITE_MAIN_LINE_L1, funcCompToString); + int currentLine = result.getEvent().getFrame().getLine(); + assertTrue(currentLine == COMPOSITE_TOSTRING_LINE_1); //first line of toString() + } + + @Test + public void stepIntoRecursiveMethod() throws Throwable { + //Step to the recursive method + ResultContext result = stepIntoSelectionBase(SRC_COMPOSITE, COMPOSITE_MAIN_LINE_L4, funcCompToString_c); + int currentLine = result.getEvent().getFrame().getLine(); + assertTrue(currentLine == COMPOSITE_TOSTRING_C_LINE_1); + + //Move away from the first line of the method to validate a successful recursive return to this location + int offset = 3; + result = runToLine(result.getContext(), SRC_COMPOSITE, COMPOSITE_TOSTRING_C_LINE_1 + offset); + currentLine = result.getEvent().getFrame().getLine(); + assertTrue(currentLine == COMPOSITE_TOSTRING_C_LINE_1 + offset); + + //Step into selection to trigger the recursive call + result = stepIntoSelectionBase(SRC_COMPOSITE, COMPOSITE_MAIN_LINE_S5, funcCompToString_c, false, false, result.getContext()); + currentLine = result.getEvent().getFrame().getLine(); + + //Assert going back to the top of the same function + assertTrue(currentLine == COMPOSITE_TOSTRING_C_LINE_1); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/tests_6_8/StepIntoSelectionTest_6_8.java b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/tests_6_8/StepIntoSelectionTest_6_8.java new file mode 100644 index 00000000000..5095ab20b99 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/tests_6_8/StepIntoSelectionTest_6_8.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * 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: + * Alvaro Sanchez-Leon (Ericsson AB) - Support for Step into selection (bug 244865) + *******************************************************************************/ +package org.eclipse.cdt.tests.dsf.gdb.tests.tests_6_8; + +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.eclipse.cdt.tests.dsf.gdb.tests.StepIntoSelectionTest; +import org.junit.runner.RunWith; + +@RunWith(BackgroundRunner.class) +public class StepIntoSelectionTest_6_8 extends StepIntoSelectionTest { + @Override + protected void setGdbVersion() { + setGdbProgramNamesLaunchAttributes(ITestConstants.SUFFIX_GDB_6_8); + } + + @Override + protected void setLaunchAttributes() { + super.setLaunchAttributes(); + + setLaunchAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_NON_STOP, false); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/tests_6_8/Suite_6_8.java b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/tests_6_8/Suite_6_8.java index a5b180b6d80..93330c6a4b9 100644 --- a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/tests_6_8/Suite_6_8.java +++ b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/tests_6_8/Suite_6_8.java @@ -44,6 +44,7 @@ import org.junit.runners.Suite; PostMortemCoreTest_6_8.class, CommandTimeoutTest_6_8.class, Suite_Sessionless_Tests.class, + StepIntoSelectionTest_6_8.class, /* Add your test class here */ }) diff --git a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/tests_7_0/StepIntoSelectionTest_7_0_NS.java b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/tests_7_0/StepIntoSelectionTest_7_0_NS.java new file mode 100644 index 00000000000..737a015726a --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/tests_7_0/StepIntoSelectionTest_7_0_NS.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * 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: + * Alvaro Sanchez-Leon (Ericsson AB) - Support for Step into selection (bug 244865) + *******************************************************************************/ +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.eclipse.cdt.tests.dsf.gdb.tests.StepIntoSelectionTest; +import org.junit.runner.RunWith; + +@RunWith(BackgroundRunner.class) +public class StepIntoSelectionTest_7_0_NS extends StepIntoSelectionTest { + @Override + protected void setGdbVersion() { + setGdbProgramNamesLaunchAttributes(ITestConstants.SUFFIX_GDB_7_0); + } + + @Override + protected void setLaunchAttributes() { + super.setLaunchAttributes(); + + setLaunchAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_NON_STOP, false); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/tests_7_0/Suite_7_0.java b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/tests_7_0/Suite_7_0.java index 7b332907ccb..602c08b397c 100644 --- a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/tests_7_0/Suite_7_0.java +++ b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/tests_7_0/Suite_7_0.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2009, 2010 Ericsson and others. + * Copyright (c) 2009, 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 @@ -46,7 +46,9 @@ import org.junit.runners.Suite; PostMortemCoreTest_7_0.class, CommandTimeoutTest_7_0.class, GDBMultiNonStopRunControlTest_7_0.class, - Suite_Sessionless_Tests.class, + Suite_Sessionless_Tests.class, + StepIntoSelectionTest_7_0_NS.class, + /* Add your test class here */ }) diff --git a/dsf/org.eclipse.cdt.dsf.ui/META-INF/MANIFEST.MF b/dsf/org.eclipse.cdt.dsf.ui/META-INF/MANIFEST.MF index 3050f5a8653..3a1a74dfcb1 100644 --- a/dsf/org.eclipse.cdt.dsf.ui/META-INF/MANIFEST.MF +++ b/dsf/org.eclipse.cdt.dsf.ui/META-INF/MANIFEST.MF @@ -3,7 +3,7 @@ Bundle-ManifestVersion: 2 Bundle-Name: %pluginName Bundle-Vendor: %providerName Bundle-SymbolicName: org.eclipse.cdt.dsf.ui;singleton:=true -Bundle-Version: 2.3.0.qualifier +Bundle-Version: 2.4.0.qualifier Bundle-Activator: org.eclipse.cdt.dsf.internal.ui.DsfUIPlugin Bundle-Localization: plugin Require-Bundle: org.eclipse.ui;bundle-version="3.5.0", @@ -35,6 +35,7 @@ Export-Package: org.eclipse.cdt.dsf.debug.internal.ui;x-internal:=true, org.eclipse.cdt.dsf.debug.internal.ui.disassembly.text;x-internal:=true, org.eclipse.cdt.dsf.debug.internal.ui.disassembly.util;x-internal:=true, org.eclipse.cdt.dsf.debug.internal.ui.preferences;x-friends:="org.eclipse.cdt.dsf.gdb.ui", + org.eclipse.cdt.dsf.debug.internal.ui.sourcelookup;x-internal:=true, org.eclipse.cdt.dsf.debug.internal.ui.viewmodel;x-friends:="org.eclipse.cdt.dsf.gdb.ui", org.eclipse.cdt.dsf.debug.internal.ui.viewmodel.actions;x-friends:="org.eclipse.cdt.dsf.gdb.ui", org.eclipse.cdt.dsf.debug.internal.ui.viewmodel.detailsupport;x-internal:=true, diff --git a/dsf/org.eclipse.cdt.dsf.ui/plugin.properties b/dsf/org.eclipse.cdt.dsf.ui/plugin.properties index 8882a84afe0..2d4b8761371 100644 --- a/dsf/org.eclipse.cdt.dsf.ui/plugin.properties +++ b/dsf/org.eclipse.cdt.dsf.ui/plugin.properties @@ -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 @@ -9,6 +9,7 @@ # Wind River Systems - initial API and implementation # IBM Corporation # Patrick Chuong (Texas Instruments) - Pin and Clone Supports (331781) +# Alvaro Sanchez-Leon (Ericsson AB) - Support for Step into selection (bug 244865) ############################################################################### pluginName=Debugger Services Framework UI providerName=Eclipse CDT @@ -75,3 +76,10 @@ OpenNewView.name = Open New View disassemblyRulerColumn.addresses=Addresses disassemblyRulerColumn.functionOffsets=Function Offsets disassemblyRulerColumn.opcodes=Opcodes + +# Step into selection +stepIntoSelectionHyperlinkDetector.label = C/C++ Step Into Selection +stepIntoSelectionHyperlinkDetector.description = Performs the step into selection command on demand via a hyperlink + + + diff --git a/dsf/org.eclipse.cdt.dsf.ui/plugin.xml b/dsf/org.eclipse.cdt.dsf.ui/plugin.xml index bfa8abf7798..c7565f7e41a 100644 --- a/dsf/org.eclipse.cdt.dsf.ui/plugin.xml +++ b/dsf/org.eclipse.cdt.dsf.ui/plugin.xml @@ -106,8 +106,8 @@ class="org.eclipse.cdt.dsf.debug.internal.ui.viewmodel.actions.UpdatePoliciesContribution"> </dynamic> </menu> - </menuContribution> - + </menuContribution> + <!-- Registers view menu commands --> <menuContribution locationURI="menu:org.eclipse.debug.ui.RegisterView?after=additions"> @@ -340,9 +340,8 @@ </count> </with> </activeWhen> - </handler> + </handler> </extension> - <extension point="org.eclipse.core.expressions.definitions"> <definition id="org.eclipse.cdt.dsf.debug.ui.testIsUpdateModesActionSetActive"> <and> @@ -527,7 +526,7 @@ commandId="org.eclipse.cdt.dsf.debug.ui.disassembly.commands.gotoAddress" schemeId="org.eclipse.ui.defaultAcceleratorConfiguration"/> </extension> - + <extension point="org.eclipse.ui.contexts"> <context @@ -878,5 +877,17 @@ </targetClass> </column> </extension> - + <!-- Hyperlinking support --> + <extension + point="org.eclipse.ui.workbench.texteditor.hyperlinkDetectors"> + <hyperlinkDetector + activate="true" + class="org.eclipse.cdt.dsf.debug.ui.actions.DsfStepIntoSelectionHyperlinkDetector" + description="%stepIntoSelectionHyperlinkDetector.description" + id="org.eclipse.cdt.dsf.debug.ui.hyperlinkdetector.stepIntoSelection" + modifierKeys="M1+M3" + name="%stepIntoSelectionHyperlinkDetector.label" + targetId="org.eclipse.cdt.ui.cCode"> + </hyperlinkDetector> + </extension> </plugin> diff --git a/dsf/org.eclipse.cdt.dsf.ui/pom.xml b/dsf/org.eclipse.cdt.dsf.ui/pom.xml index 3fbd4cee479..b3f322f97e2 100644 --- a/dsf/org.eclipse.cdt.dsf.ui/pom.xml +++ b/dsf/org.eclipse.cdt.dsf.ui/pom.xml @@ -11,7 +11,7 @@ <relativePath>../../pom.xml</relativePath> </parent> - <version>2.3.0-SNAPSHOT</version> + <version>2.4.0-SNAPSHOT</version> <artifactId>org.eclipse.cdt.dsf.ui</artifactId> <packaging>eclipse-plugin</packaging> </project> diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/Messages.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/Messages.java index f28a4ab53a5..0761ffcf449 100644 --- a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/Messages.java +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/Messages.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2009, 2010 Wind River Systems, Inc. and others. + * Copyright (c) 2009, 2013 Wind River Systems, Inc. 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 * Patrick Chuong (Texas Instruments) - Bug 315446: Invalid event breakpoint type (group) name + * Alvaro Sanchez-Leon (Ericsson AB) - Support for Step into selection (bug 244865) *******************************************************************************/ package org.eclipse.cdt.dsf.debug.internal.ui; @@ -16,6 +17,7 @@ import org.eclipse.osgi.util.NLS; public class Messages extends NLS { public static String ToggleBreakpointsTargetFactory_description; public static String ToggleBreakpointsTargetFactory_name; + public static String DsfUIStepIntoEditorSelection; static { // initialize resource bundle diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/Messages.properties b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/Messages.properties index 1faea82e179..37451dbd583 100644 --- a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/Messages.properties +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/Messages.properties @@ -1,5 +1,5 @@ ############################################################################### -# Copyright (c) 2009, 2010 Wind River Systems and others. +# Copyright (c) 2009, 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,7 +8,10 @@ # Contributors: # Wind River Systems - initial API and implementation # Ericsson - added Tracepoint support +# Ericsson - added Step into selection support ############################################################################### ToggleBreakpointsTargetFactory_description=Standard C/C++ breakpoint type. ToggleBreakpointsTargetFactory_name=C/C++ Breakpoints + +DsfUIStepIntoEditorSelection=Step Into diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/sourcelookup/DsfSourceSelectionResolver.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/sourcelookup/DsfSourceSelectionResolver.java new file mode 100644 index 00000000000..aa1f5ab0657 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/sourcelookup/DsfSourceSelectionResolver.java @@ -0,0 +1,226 @@ +/******************************************************************************* + * Copyright (c) 2013 Ericsson AB 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: + * Alvaro Sanchez-Leon (Ericsson AB) - Support for Step into selection (bug 244865) + *******************************************************************************/ +package org.eclipse.cdt.dsf.debug.internal.ui.sourcelookup; + +import org.eclipse.cdt.core.model.IFunctionDeclaration; +import org.eclipse.cdt.debug.internal.ui.CDebugUIUtils; +import org.eclipse.cdt.dsf.internal.ui.DsfUIPlugin; +import org.eclipse.cdt.internal.ui.editor.CEditor; +import org.eclipse.cdt.internal.ui.editor.SelectionToDeclarationJob; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.ITextSelection; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.swt.widgets.Display; +import org.eclipse.ui.IEditorInput; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.IWorkbench; +import org.eclipse.ui.IWorkbenchWindow; +import org.eclipse.ui.editors.text.TextEditor; +import org.eclipse.ui.texteditor.ITextEditor; + +public class DsfSourceSelectionResolver implements Runnable { + private ITextEditor fEditorPage = null; + private ITextSelection fSelection = null; + private LineLocation fLineLocation = new LineLocation(); + private IFunctionDeclaration fFunction = null; + private boolean fSuccessful = false; + + public class LineLocation { + private String fileName = null; + private int lineNumber = 0; + + public String getFileName() { + return fileName; + } + + public void setFileName(String fileName) { + this.fileName = fileName; + } + + public int getLineNumber() { + return lineNumber; + } + + public void setLineNumber(int lineNumber) { + this.lineNumber = lineNumber; + } + } + + private interface ResolveEditorRunnable extends Runnable { + TextEditor getEditor(); + } + + public DsfSourceSelectionResolver() { + + } + + public DsfSourceSelectionResolver(ITextEditor editor, ITextSelection selection) { + fEditorPage = editor; + fSelection = selection; + } + + /* (non-Javadoc) + * @see org.eclipse.cdt.dsf.debug.internal.ui.sourcelookup.IDsfSourceSelectionResolver#run() + */ + @Override + public void run() { + fEditorPage = resolveEditor(); + if (fEditorPage != null) { + ITextSelection selection = resolveSelection(); + if (selection != null) { + IFunctionDeclaration[] selectedFunctions = resolveSelectedFunction(selection); + + IFunctionDeclaration selFunction = null; + if (selectedFunctions == null || selectedFunctions.length != 1 || selectedFunctions[0] == null) { + //Unable to resolve selection to a function + return; + } else { + // Continue as expected + selFunction = selectedFunctions[0]; + } + + LineLocation selectedLine = resolveSelectedLine(); + if (selectedLine == null) { + // Unable to resolve the selected line + return; + } + + fLineLocation = selectedLine; + fFunction = selFunction; + fSuccessful = true; + } + } + } + + public ITextEditor resolveEditor() { + if (fEditorPage != null) { + return fEditorPage; + } + + final IWorkbench wb = DsfUIPlugin.getDefault().getWorkbench(); + // Run in UI thread to access UI resources + ResolveEditorRunnable reditorRunnable = new ResolveEditorRunnable() { + TextEditor result = null; + + @Override + public void run() { + IWorkbenchWindow win = wb.getActiveWorkbenchWindow(); + if (win == null || win.getActivePage() == null || win.getActivePage().getActiveEditor() == null) { + result = null; + } else { + IEditorPart editorPart = win.getActivePage().getActiveEditor(); + if (editorPart instanceof CEditor) { + result = (TextEditor) win.getActivePage().getActiveEditor(); + } + } + } + + @Override + public TextEditor getEditor() { + return result; + } + }; + + Display.getDefault().syncExec(reditorRunnable); + return reditorRunnable.getEditor(); + } + + private LineLocation resolveSelectedLine() { + String errorMessage = ""; //$NON-NLS-1$ + IEditorInput input = fEditorPage.getEditorInput(); + if (input == null) { + errorMessage = "Invalid Editor input on selection"; //$NON-NLS-1$ + } else { + IDocument document = fEditorPage.getDocumentProvider().getDocument(input); + if (document == null) { + errorMessage = "Invalid Editor Document input on selection"; //$NON-NLS-1$ + } else { + ITextSelection selection = resolveSelection(); + if (selection == null) { + errorMessage = "Invalid selection. Only textual selections are supported"; //$NON-NLS-1$ + } else { + String fileName = null; + try { + fileName = CDebugUIUtils.getEditorFilePath(input); + } catch (CoreException e) { + // unable to resolve the path + DsfUIPlugin.log(e); + return null; + } + + if (fileName == null) { + errorMessage = "Unable to resolve fileName from selection"; //$NON-NLS-1$ + DsfUIPlugin.logErrorMessage(errorMessage); + } else { + // Resolve the values + LineLocation lineLocation = new LineLocation(); + + lineLocation.setFileName(fileName); + lineLocation.setLineNumber(selection.getStartLine() + 1); + return lineLocation; + } + } + } + } + + DsfUIPlugin.logErrorMessage(errorMessage); + return null; + } + + public ITextSelection resolveSelection() { + if (fSelection != null) { + //Value received at construction time + return fSelection; + } + + ISelection selection = fEditorPage.getEditorSite().getSelectionProvider().getSelection(); + if (selection instanceof ITextSelection) { + return (ITextSelection) selection; + } + + return null; + } + + private IFunctionDeclaration[] resolveSelectedFunction(ITextSelection textSelection) { + if (textSelection != null) { + SelectionToDeclarationJob job; + try { + job = new SelectionToDeclarationJob(fEditorPage, textSelection); + job.schedule(); + job.join(); + } catch (CoreException e1) { + DsfUIPlugin.log(e1); + return null; + } catch (InterruptedException e) { + DsfUIPlugin.log(e); + return null; + } + + //fetch the result + return job.getSelectedFunctions(); + } + + return null; + } + + public LineLocation getLineLocation() { + return fLineLocation; + } + + public IFunctionDeclaration getFunction() { + return fFunction; + } + + public boolean isSuccessful() { + return fSuccessful; + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/actions/DsfStepIntoSelectionCommand.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/actions/DsfStepIntoSelectionCommand.java new file mode 100644 index 00000000000..7ec6ce2e0bb --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/actions/DsfStepIntoSelectionCommand.java @@ -0,0 +1,195 @@ +/******************************************************************************* + * Copyright (c) 2013 Ericsson AB 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: + * Alvaro Sanchez-Leon (Ericsson AB) - Support for Step into selection (bug 244865) + *******************************************************************************/ +package org.eclipse.cdt.dsf.debug.ui.actions; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.RejectedExecutionException; + +import org.eclipse.cdt.core.model.IFunctionDeclaration; +import org.eclipse.cdt.debug.core.model.IStepIntoSelectionHandler; +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants; +import org.eclipse.cdt.dsf.concurrent.Query; +import org.eclipse.cdt.dsf.datamodel.DMContexts; +import org.eclipse.cdt.dsf.debug.internal.ui.sourcelookup.DsfSourceSelectionResolver; +import org.eclipse.cdt.dsf.debug.internal.ui.sourcelookup.DsfSourceSelectionResolver.LineLocation; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext; +import org.eclipse.cdt.dsf.debug.service.IRunControl3; +import org.eclipse.cdt.dsf.internal.ui.DsfUIPlugin; +import org.eclipse.cdt.dsf.service.DsfServicesTracker; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.IDMVMContext; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.debug.core.DebugException; +import org.eclipse.debug.core.IRequest; +import org.eclipse.debug.core.commands.AbstractDebugCommand; +import org.eclipse.debug.core.commands.IDebugCommandRequest; +import org.eclipse.debug.core.commands.IEnabledStateRequest; +import org.eclipse.debug.ui.DebugUITools; +import org.eclipse.debug.ui.IDebugUIConstants; +import org.eclipse.swt.widgets.Display; + +/** + * @since 2.4 + */ +public class DsfStepIntoSelectionCommand extends AbstractDebugCommand implements IStepIntoSelectionHandler, IDsfStepIntoSelection { + private final DsfServicesTracker fTracker; + public DsfStepIntoSelectionCommand(DsfSession session) { + fTracker = new DsfServicesTracker(DsfUIPlugin.getBundleContext(), session.getId()); + } + + public void dispose() { + fTracker.dispose(); + } + + @Override + protected void doExecute(Object[] targets, IProgressMonitor monitor, IRequest request) throws CoreException { + // No multiple selections allowed for Step into selection + if (targets.length != 1) { + return; + } + + final IExecutionDMContext dmc = DMContexts.getAncestorOfType(((IDMVMContext) targets[0]).getDMContext(), IExecutionDMContext.class); + if (dmc == null) { + return; + } + + DsfSourceSelectionResolver resolveSelection = new DsfSourceSelectionResolver(); + // Resolve UI selection from the the UI thread + Display.getDefault().syncExec(resolveSelection); + if (resolveSelection.isSuccessful()) { + LineLocation location = resolveSelection.getLineLocation(); + runToSelection(location.getFileName(), location.getLineNumber(), resolveSelection.getFunction(), dmc); + } else { + DsfUIPlugin.debug("DSfStepIntoSelectionCommand: Unable to resolve a selected function"); //$NON-NLS-1$ + } + } + + @Override + protected boolean isExecutable(Object[] targets, IProgressMonitor monitor, IEnabledStateRequest request) throws CoreException { + // No multiple selections allowed for Step into selection + if (targets.length != 1) { + return false; + } + + final IExecutionDMContext dmc = DMContexts.getAncestorOfType(((IDMVMContext) targets[0]).getDMContext(), IExecutionDMContext.class); + if (dmc == null) { + return false; + } + + return isExecutable(dmc, fTracker); + } + + private boolean isExecutable(final IExecutionDMContext dmc, final DsfServicesTracker tracker) { + final DsfSession session = DsfSession.getSession(dmc.getSessionId()); + if (session != null && session.isActive()) { + try { + Query<Boolean> query = new Query<Boolean>() { + @Override + protected void execute(DataRequestMonitor<Boolean> rm) { + IRunControl3 runControl = tracker.getService(IRunControl3.class); + if (runControl != null) { + // The selection may not be up to date, this is indicated with + // the selectedFunction being set to null + runControl.canStepIntoSelection(dmc, null, 0, null, rm); + } else { + rm.setData(false); + rm.done(); + } + } + }; + + session.getExecutor().execute(query); + return query.get(); + } catch (InterruptedException e) { + } catch (ExecutionException e) { + } + } + + return false; + } + + @Override + protected Object getTarget(Object element) { + if (element instanceof IDMVMContext) { + return element; + } + return null; + } + + @Override + protected boolean isRemainEnabled(IDebugCommandRequest request) { + return true; + } + + @Override + public boolean isExecutable(final IExecutionDMContext dmc) { + if (dmc == null) { + return false; + } + + DsfServicesTracker tracker = new DsfServicesTracker(DsfUIPlugin.getBundleContext(), dmc.getSessionId()); + boolean result = isExecutable(dmc, tracker); + tracker.dispose(); + return result; + } + + @Override + public void runToSelection(final String fileName, final int lineLocation, final IFunctionDeclaration selectedFunction, final IExecutionDMContext dmc) { + final DsfSession session = DsfSession.getSession(dmc.getSessionId()); + if (session != null && session.isActive()) { + Throwable exception = null; + try { + Query<Object> query = new Query<Object>() { + @Override + protected void execute(final DataRequestMonitor<Object> rm) { + DsfServicesTracker tracker = new DsfServicesTracker(DsfUIPlugin.getBundleContext(), session.getId()); + + boolean skipBreakpoints = DebugUITools.getPreferenceStore().getBoolean(IDebugUIConstants.PREF_SKIP_BREAKPOINTS_DURING_RUN_TO_LINE); + + IRunControl3 runControl = tracker.getService(IRunControl3.class); + tracker.dispose(); + StringBuilder eMessage = null; + if (runControl != null) { + runControl.stepIntoSelection(dmc, fileName, lineLocation, skipBreakpoints, selectedFunction, rm); + return; + } else { + eMessage = new StringBuilder("IRunControl3 service not available"); //$NON-NLS-1$ + } + + // Either runControl is null or an Exception has occurred + rm.setStatus(new Status(IStatus.ERROR, DsfUIPlugin.PLUGIN_ID, IDsfStatusConstants.NOT_SUPPORTED, eMessage.toString(), null)); + rm.done(); + } + }; + + session.getExecutor().execute(query); + query.get(); + } catch (RejectedExecutionException e) { + exception = e; + } catch (InterruptedException e) { + exception = e; + } catch (ExecutionException e) { + exception = e; + } + + if (exception != null) { + DsfUIPlugin.log(new DebugException(new Status(IStatus.ERROR, DsfUIPlugin.PLUGIN_ID, DebugException.REQUEST_FAILED, "Failed executing Step into Selection", exception)));//$NON-NLS-1$ + } + } else { + DsfUIPlugin.log(new DebugException(new Status(IStatus.ERROR, DsfUIPlugin.PLUGIN_ID, DebugException.REQUEST_FAILED, "Debug session is not active", null))); //$NON-NLS-1$ + } + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/actions/DsfStepIntoSelectionHyperlinkDetector.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/actions/DsfStepIntoSelectionHyperlinkDetector.java new file mode 100644 index 00000000000..5c2a5a09d03 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/actions/DsfStepIntoSelectionHyperlinkDetector.java @@ -0,0 +1,239 @@ +/******************************************************************************* + * Copyright (c) 2013 Ericsson AB 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: + * Alvaro Sanchez-Leon (Ericsson AB) - Support for Step into selection (bug 244865) + *******************************************************************************/ +package org.eclipse.cdt.dsf.debug.ui.actions; + +import org.eclipse.cdt.core.model.ICLanguageKeywords; +import org.eclipse.cdt.core.model.ILanguage; +import org.eclipse.cdt.core.model.IWorkingCopy; +import org.eclipse.cdt.dsf.datamodel.DMContexts; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.debug.internal.ui.Messages; +import org.eclipse.cdt.dsf.debug.internal.ui.sourcelookup.DsfSourceSelectionResolver; +import org.eclipse.cdt.dsf.debug.internal.ui.sourcelookup.DsfSourceSelectionResolver.LineLocation; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext; +import org.eclipse.cdt.dsf.internal.ui.DsfUIPlugin; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.IDMVMContext; +import org.eclipse.cdt.internal.ui.editor.CEditor; +import org.eclipse.cdt.internal.ui.text.CWordFinder; +import org.eclipse.cdt.ui.CUIPlugin; +import org.eclipse.cdt.ui.text.ICPartitions; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.debug.ui.DebugUITools; +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IRegion; +import org.eclipse.jface.text.ITextSelection; +import org.eclipse.jface.text.ITextViewer; +import org.eclipse.jface.text.Region; +import org.eclipse.jface.text.TextSelection; +import org.eclipse.jface.text.TextUtilities; +import org.eclipse.jface.text.hyperlink.AbstractHyperlinkDetector; +import org.eclipse.jface.text.hyperlink.IHyperlink; +import org.eclipse.ui.texteditor.ITextEditor; + +/** + * @since 2.4 + * + */ +public class DsfStepIntoSelectionHyperlinkDetector extends AbstractHyperlinkDetector { + private class DsfStepIntoSelectionHyperlink implements IHyperlink { + + private ITextSelection fSelection = null; + private IDsfStepIntoSelection fStepIntoSelectionCommand = null; + private DsfSourceSelectionResolver fSelectionResolver = null; + + /** + * Constructor + * + * @param stepIntoSelectionCommand + * @param region + */ + public DsfStepIntoSelectionHyperlink(DsfSourceSelectionResolver selectionResolver, IDsfStepIntoSelection stepIntoSelectionCommand) { + fSelection = selectionResolver.resolveSelection(); + fStepIntoSelectionCommand = stepIntoSelectionCommand; + fSelectionResolver = selectionResolver; + } + + /** + * @see org.eclipse.jface.text.hyperlink.IHyperlink#getHyperlinkRegion() + */ + @Override + public IRegion getHyperlinkRegion() { + return new Region(fSelection.getOffset(), fSelection.getLength()); + } + + /** + * @see org.eclipse.jface.text.hyperlink.IHyperlink#getHyperlinkText() + */ + @Override + public String getHyperlinkText() { + return Messages.DsfUIStepIntoEditorSelection; + } + + /** + * @see org.eclipse.jface.text.hyperlink.IHyperlink#getTypeLabel() + */ + @Override + public String getTypeLabel() { + return null; + } + + /** + * @see org.eclipse.jface.text.hyperlink.IHyperlink#open() + */ + @Override + public void open() { + // Resolve the debug context + final IExecutionDMContext dmc = resolveDebugContext(); + if (fSelectionResolver.isSuccessful() && dmc != null) { + LineLocation location = fSelectionResolver.getLineLocation(); + fStepIntoSelectionCommand.runToSelection(location.getFileName(), location.getLineNumber(), fSelectionResolver.getFunction(), dmc); + } else { + String message = null; + if (dmc == null) { + message = "DSfStepIntoSelection: Unable to resolve the debug context"; //$NON-NLS-1$ + } else { + message = "DSfStepIntoSelection: Unable to resolve a selected function"; //$NON-NLS-1$ + } + DsfUIPlugin.debug(message); + } + } + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.text.hyperlink.IHyperlinkDetector#detectHyperlinks(org.eclipse.jface.text.ITextViewer, org.eclipse.jface.text.IRegion, boolean) + */ + @Override + public IHyperlink[] detectHyperlinks(ITextViewer textViewer, final IRegion region, boolean canShowMultipleHyperlinks) { + // Only valid in the context of a selection within the CEditor + ITextEditor editor = (ITextEditor) getAdapter(ITextEditor.class); + if (editor == null || region == null || !(editor instanceof CEditor)) + return null; + + ITextSelection selection = resolveSelection(editor, region); + if (selection == null) { + return null; + } + + // Shall only enable hyper link step into selection within a cdt debug execution context + IExecutionDMContext dmc = resolveDebugContext(); + if (dmc == null) { + return null; + } + + final DsfSession session = DsfSession.getSession(dmc.getSessionId()); + if (session == null || !session.isActive()) { + return null; + } + + IDsfStepIntoSelection stepIntoSelectionCommand = (IDsfStepIntoSelection) session.getModelAdapter(IDsfStepIntoSelection.class); + if (stepIntoSelectionCommand == null) { + return null; + } + + if (!stepIntoSelectionCommand.isExecutable(dmc)) { + return null; + } + + DsfSourceSelectionResolver functionResolver = new DsfSourceSelectionResolver(editor, selection); + functionResolver.run(); + // Resolve to a selected function + if (!functionResolver.isSuccessful()) { + // We are not pointing to a valid function + return null; + } + + return new IHyperlink[] { new DsfStepIntoSelectionHyperlink(functionResolver, stepIntoSelectionCommand) }; + } + + private ITextSelection resolveSelection(ITextEditor editor, IRegion region) { + ITextSelection selection = null; + if (editor != null) { + IDocument document = editor.getDocumentProvider().getDocument(editor.getEditorInput()); + final IWorkingCopy workingCopy = CUIPlugin.getDefault().getWorkingCopyManager().getWorkingCopy(editor.getEditorInput()); + + if (document != null && workingCopy != null) { + // Check partition type. + String partitionType; + try { + partitionType = TextUtilities.getContentType(document, ICPartitions.C_PARTITIONING, region.getOffset(), false); + if (IDocument.DEFAULT_CONTENT_TYPE.equals(partitionType)) { + // Regular code i.e. Not a Preprocessor directive. + IRegion wregion = getIdentifier(document, region.getOffset(), workingCopy.getLanguage()); + if (wregion != null) { + selection = new TextSelection(document, wregion.getOffset(), wregion.getLength()); + } + } + } catch (BadLocationException e) { + // Ignore to return null + } catch (CoreException e) { + // Ignore to return null + } + } + } + + return selection; + } + + /** + * Returns the identifier at the given offset, or {@code null} if the there is no identifier at the offset. + */ + private static IRegion getIdentifier(IDocument document, int offset, ILanguage language) throws BadLocationException { + @SuppressWarnings("restriction") + IRegion wordRegion = CWordFinder.findWord(document, offset); + if (wordRegion != null && wordRegion.getLength() > 0) { + String word = document.get(wordRegion.getOffset(), wordRegion.getLength()); + if (!Character.isDigit(word.charAt(0)) && !isLanguageKeyword(language, word)) { + return wordRegion; + } + } + return null; + } + + private static boolean isLanguageKeyword(ILanguage lang, String word) { + ICLanguageKeywords keywords = (ICLanguageKeywords) lang.getAdapter(ICLanguageKeywords.class); + if (keywords != null) { + for (String keyword : keywords.getKeywords()) { + if (keyword.equals(word)) + return true; + } + for (String type : keywords.getBuiltinTypes()) { + if (type.equals(word)) + return true; + } + for (String keyword : keywords.getPreprocessorKeywords()) { + if (keyword.charAt(0) == '#' && keyword.length() == word.length() + 1 && keyword.regionMatches(1, word, 0, word.length())) { + return true; + } + } + } + return false; + } + + private IExecutionDMContext resolveDebugContext() { + IExecutionDMContext execContext = null; + IAdaptable adaptableContext = DebugUITools.getDebugContext(); + IDMContext debugContext = null; + if (adaptableContext instanceof IDMVMContext) { + debugContext = ((IDMVMContext) adaptableContext).getDMContext(); + } + + if (debugContext != null) { + execContext = DMContexts.getAncestorOfType(debugContext, IExecutionDMContext.class); + } + + return execContext; + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/actions/IDsfStepIntoSelection.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/actions/IDsfStepIntoSelection.java new file mode 100644 index 00000000000..43ca6cd11c3 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/actions/IDsfStepIntoSelection.java @@ -0,0 +1,36 @@ +/******************************************************************************* + * Copyright (c) 2013 Ericsson AB 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: + * Alvaro Sanchez-Leon (Ericsson AB) - Support for Step into selection (bug 244865) + *******************************************************************************/ +package org.eclipse.cdt.dsf.debug.ui.actions; + +import org.eclipse.cdt.core.model.IFunctionDeclaration; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext; + +/** + * @since 2.4 + */ +public interface IDsfStepIntoSelection { + + /** + * Checks if within a state to perform step into selection + * @param dmc + * @return + */ + public boolean isExecutable(final IExecutionDMContext dmc); + + /** + * Carries out the actual step into selection action to the specified function location + * @param fileName + * @param lineLocation + * @param selectedFunction + * @param context + */ + public void runToSelection(final String fileName, final int lineLocation, final IFunctionDeclaration selectedFunction, final IExecutionDMContext context); +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IRunControl3.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IRunControl3.java new file mode 100644 index 00000000000..f67f709781d --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IRunControl3.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2013 Ericsson AB 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: + * Alvaro Sanchez-Leon (Ericsson AB) - Support for Step into selection (bug 244865) + *******************************************************************************/ +package org.eclipse.cdt.dsf.debug.service; + +import org.eclipse.cdt.core.model.IFunctionDeclaration; +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; + +/** + * This interface extends IRunControl2 to let a service support the + * "Step into selection" command. + * @since 2.4 + */ +public interface IRunControl3 extends IRunControl2 { + /** + * Returns whether the service is in the state to execute 'Step into selection' + * for the specified context + * + * @param context the execution DM context + * @param sourceFile the source file path, mapped to a debugger path if possible, invalid if selectedFunction is Null + * @param lineNumber the line number of the source file where the user selected the target function, invalid if selectedFunction is Null + * @param selectedFunction The target function to step into <br>NOTE: a null value shall disregard linenumber and sourceFile + * @param rm the DataRequestMonitor that will return the result + */ + void canStepIntoSelection(IExecutionDMContext context, String sourceFile, int lineNumber, + IFunctionDeclaration selectedFunction, DataRequestMonitor<Boolean> rm); + + /** + * Request to run the program forward into the specified function + * if the current stop location is not at the specified lineNumber and + * skipBreakpoints is false, any other breakpoint found before the specified line number will stop this + * command; while if skipBreakpoints is true, the operation will ignore + * other breakpoints and continue until the specified location. + * + * @param context the execution DM context + * @param sourceFile the source file path, mapped to a debugger path if possible. + * @param lineNumber the line number of the source file where the user selected the target function + * @param skipBreakpoints skip breakpoints while performing this operation + * @param selectedFunction the target function to step into + * @param rm the DataRequestMonitor that will return the result + */ + void stepIntoSelection (IExecutionDMContext context, String sourceFile, int lineNumber, + boolean skipBreakpoints, IFunctionDeclaration selectedFunction, RequestMonitor rm); +} |