diff --git a/debug/org.eclipse.cdt.debug.core/src/org/eclipse/cdt/debug/core/sourcelookup/MappingSourceContainer.java b/debug/org.eclipse.cdt.debug.core/src/org/eclipse/cdt/debug/core/sourcelookup/MappingSourceContainer.java index 5f4e771ba0a..763cf22edbd 100644 --- a/debug/org.eclipse.cdt.debug.core/src/org/eclipse/cdt/debug/core/sourcelookup/MappingSourceContainer.java +++ b/debug/org.eclipse.cdt.debug.core/src/org/eclipse/cdt/debug/core/sourcelookup/MappingSourceContainer.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2004, 2012 QNX Software Systems and others. + * Copyright (c) 2004, 2015 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 @@ -40,6 +40,7 @@ public class MappingSourceContainer extends AbstractSourceContainer implements I private String fName; private ArrayList fContainers; + private boolean fIsMappingWithBackendEnabled = true; /** * Constructor for MappingSourceContainer. @@ -176,6 +177,7 @@ public class MappingSourceContainer extends AbstractSourceContainer implements I public MappingSourceContainer copy() { MappingSourceContainer copy = new MappingSourceContainer(fName); + copy.setIsMappingWithBackendEnabled(isMappingWithBackendEnabled()); MapEntrySourceContainer[] entries = new MapEntrySourceContainer[fContainers.size()]; for (int i = 0; i < entries.length; ++i) { copy.addMapEntry(fContainers.get(i).copy()); @@ -209,4 +211,29 @@ public class MappingSourceContainer extends AbstractSourceContainer implements I } return result; } + + /** + * Return true if the user has allowed this mapping container + * to be mapped with the backend (e.g. GDB's set substitute-path) instead of + * using {@link #findSourceElements(String)} and + * {@link #getCompilationPath(String)} + *

+ * The default if otherwise unspecified is to allow the GDB backend to + * handle the substitution. + * + * @since 8.0 + */ + public boolean isMappingWithBackendEnabled() { + return fIsMappingWithBackendEnabled; + } + + /** + * Set whether mapping is enabled. See + * {@link #isMappingWithBackendEnabled()} + * + * @since 8.0 + */ + public void setIsMappingWithBackendEnabled(boolean isMappingWithBackendEnabled) { + fIsMappingWithBackendEnabled = isMappingWithBackendEnabled; + } } diff --git a/debug/org.eclipse.cdt.debug.core/src/org/eclipse/cdt/debug/internal/core/sourcelookup/MappingSourceContainerType.java b/debug/org.eclipse.cdt.debug.core/src/org/eclipse/cdt/debug/internal/core/sourcelookup/MappingSourceContainerType.java index e7137e98ce3..b2e8171be7b 100644 --- a/debug/org.eclipse.cdt.debug.core/src/org/eclipse/cdt/debug/internal/core/sourcelookup/MappingSourceContainerType.java +++ b/debug/org.eclipse.cdt.debug.core/src/org/eclipse/cdt/debug/internal/core/sourcelookup/MappingSourceContainerType.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2004, 2012 QNX Software Systems and others. + * Copyright (c) 2004, 2015 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 @@ -30,6 +30,7 @@ public class MappingSourceContainerType extends AbstractSourceContainerTypeDeleg private final static String ELEMENT_MAPPING = "mapping"; //$NON-NLS-1$ private final static String ELEMENT_MAP_ENTRY = "mapEntry"; //$NON-NLS-1$ private final static String ATTR_NAME = "name"; //$NON-NLS-1$ + private final static String ATTR_BACKEND_ENABLED = "backend_enabled"; //$NON-NLS-1$ private final static String ATTR_MEMENTO = "memento"; //$NON-NLS-1$ /* (non-Javadoc) @@ -44,6 +45,10 @@ public class MappingSourceContainerType extends AbstractSourceContainerTypeDeleg String name = element.getAttribute(ATTR_NAME); if (name == null) name = ""; //$NON-NLS-1$ + String backendEnabled = element.getAttribute(ATTR_BACKEND_ENABLED); + // When upgrading source locator (See Bug 472765), + // do not enable backend path substitution + boolean enabled = Boolean.parseBoolean(backendEnabled); List entries = new ArrayList(); Node childNode = element.getFirstChild(); while (childNode != null) { @@ -62,6 +67,7 @@ public class MappingSourceContainerType extends AbstractSourceContainerTypeDeleg childNode = childNode.getNextSibling(); } MappingSourceContainer container = new MappingSourceContainer(name); + container.setIsMappingWithBackendEnabled(enabled); for (MapEntrySourceContainer entry : entries) { container.addMapEntry(entry); } @@ -81,6 +87,8 @@ public class MappingSourceContainerType extends AbstractSourceContainerTypeDeleg Document document = newDocument(); Element element = document.createElement(ELEMENT_MAPPING); element.setAttribute(ATTR_NAME, container.getName()); + boolean backendEnabled = ((MappingSourceContainer)container).isMappingWithBackendEnabled(); + element.setAttribute(ATTR_BACKEND_ENABLED, String.valueOf(backendEnabled)); ISourceContainer[] entries = ((MappingSourceContainer)container).getSourceContainers(); for (int i = 0; i < entries.length; ++i) { Element child = document.createElement(ELEMENT_MAP_ENTRY); diff --git a/debug/org.eclipse.cdt.debug.ui/src/org/eclipse/cdt/debug/internal/ui/sourcelookup/MappingSourceContainerDialog.java b/debug/org.eclipse.cdt.debug.ui/src/org/eclipse/cdt/debug/internal/ui/sourcelookup/MappingSourceContainerDialog.java index d8405a042e4..ee534ba8a2e 100644 --- a/debug/org.eclipse.cdt.debug.ui/src/org/eclipse/cdt/debug/internal/ui/sourcelookup/MappingSourceContainerDialog.java +++ b/debug/org.eclipse.cdt.debug.ui/src/org/eclipse/cdt/debug/internal/ui/sourcelookup/MappingSourceContainerDialog.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2009, 2012 ARM Limited and others. + * Copyright (c) 2009, 2015 ARM Limited 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 @@ -166,6 +166,7 @@ public class MappingSourceContainerDialog extends TitleAreaDialog { private MappingSourceContainer fContainer; private Text fNameText; + private Button fMappingWithBackendEnabledCheck; private TableViewer fViewer; private Button fAddButton; private Button fRemoveButton; @@ -254,6 +255,7 @@ public class MappingSourceContainerDialog extends TitleAreaDialog { createNameArea(composite); createViewer(composite); createViewerButtonBar(composite); + createMappingWithBackendEnabledArea(composite); PlatformUI.getWorkbench().getHelpSystem().setHelp(getShell(), ICDebugHelpContextIds.SOURCE_PATH_MAP_ENTRY_DIALOG); return control; @@ -266,6 +268,7 @@ public class MappingSourceContainerDialog extends TitleAreaDialog { protected void okPressed() { fOriginalContainer.clear(); fOriginalContainer.setName(fNameText.getText().trim()); + fOriginalContainer.setIsMappingWithBackendEnabled(fMappingWithBackendEnabledCheck.getSelection()); try { fOriginalContainer.addMapEntries((MapEntrySourceContainer[])fContainer.getSourceContainers()); } catch (CoreException e) { @@ -307,6 +310,18 @@ public class MappingSourceContainerDialog extends TitleAreaDialog { }); } + private void createMappingWithBackendEnabledArea(Composite parent) { + Composite composite = new Composite(parent, SWT.None); + composite.setLayout(new GridLayout(1, false)); + composite.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false, 2, 1)); + + fMappingWithBackendEnabledCheck = new Button(composite, SWT.CHECK); + fMappingWithBackendEnabledCheck.setText(SourceLookupUIMessages.PathMappingDialog_MappingWithBackendEnabled); + fMappingWithBackendEnabledCheck.setToolTipText(SourceLookupUIMessages.PathMappingDialog_MappingWithBackendEnabledTooltip); + GridData data = new GridData(SWT.FILL, SWT.CENTER, false, false); + fMappingWithBackendEnabledCheck.setLayoutData(data); + } + private void createViewer(Composite parent) { Composite tableComp = new Composite(parent, SWT.NONE); tableComp.setLayout(new GridLayout()); @@ -442,6 +457,7 @@ public class MappingSourceContainerDialog extends TitleAreaDialog { private void initialize() { fNameText.setText(fContainer.getName()); fNameText.selectAll(); + fMappingWithBackendEnabledCheck.setSelection(fContainer.isMappingWithBackendEnabled()); fViewer.setInput(fContainer); updateViewerButtons(); } diff --git a/debug/org.eclipse.cdt.debug.ui/src/org/eclipse/cdt/debug/internal/ui/sourcelookup/SourceLookupUIMessages.java b/debug/org.eclipse.cdt.debug.ui/src/org/eclipse/cdt/debug/internal/ui/sourcelookup/SourceLookupUIMessages.java index 3aa56f17b63..1270e8e9481 100644 --- a/debug/org.eclipse.cdt.debug.ui/src/org/eclipse/cdt/debug/internal/ui/sourcelookup/SourceLookupUIMessages.java +++ b/debug/org.eclipse.cdt.debug.ui/src/org/eclipse/cdt/debug/internal/ui/sourcelookup/SourceLookupUIMessages.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2004, 2010 QNX Software Systems and others. + * Copyright (c) 2004, 2015 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 @@ -44,6 +44,8 @@ public class SourceLookupUIMessages extends NLS { public static String PathMappingDialog_14; public static String PathMappingDialog_15; public static String PathMappingDialog_16; + public static String PathMappingDialog_MappingWithBackendEnabled; + public static String PathMappingDialog_MappingWithBackendEnabledTooltip; public static String RemoveAction_0; public static String SourceContainerWorkbenchAdapter_0; public static String UpAction_0; diff --git a/debug/org.eclipse.cdt.debug.ui/src/org/eclipse/cdt/debug/internal/ui/sourcelookup/SourceLookupUIMessages.properties b/debug/org.eclipse.cdt.debug.ui/src/org/eclipse/cdt/debug/internal/ui/sourcelookup/SourceLookupUIMessages.properties index 00293eac067..4c94a87ccd9 100644 --- a/debug/org.eclipse.cdt.debug.ui/src/org/eclipse/cdt/debug/internal/ui/sourcelookup/SourceLookupUIMessages.properties +++ b/debug/org.eclipse.cdt.debug.ui/src/org/eclipse/cdt/debug/internal/ui/sourcelookup/SourceLookupUIMessages.properties @@ -1,5 +1,5 @@ ############################################################################### -# Copyright (c) 2005, 2010 QNX Software Systems and others. +# Copyright (c) 2005, 2015 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 @@ -41,6 +41,8 @@ PathMappingDialog_13=&Add... PathMappingDialog_14=&Edit... PathMappingDialog_15=Re&move PathMappingDialog_16=Path Mappings +PathMappingDialog_MappingWithBackendEnabled=Enable performing the mapping with the debugger backend. +PathMappingDialog_MappingWithBackendEnabledTooltip=Allow the mapping to be performed with the debugger backend where supported. This allows any direct interaction with the backend (for example via the console) to have a consistent view of file names. RemoveAction_0=Re&move SourceContainerWorkbenchAdapter_0=Path Mapping: UpAction_0=U&p diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/FinalLaunchSequence.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/FinalLaunchSequence.java index f6b1d83705f..e3b6ae88b04 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/FinalLaunchSequence.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/FinalLaunchSequence.java @@ -36,11 +36,13 @@ import org.eclipse.cdt.dsf.concurrent.RequestMonitorWithProgress; import org.eclipse.cdt.dsf.datamodel.DataModelInitializedEvent; import org.eclipse.cdt.dsf.datamodel.IDMContext; import org.eclipse.cdt.dsf.debug.service.ISourceLookup.ISourceLookupDMContext; +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlDMContext; import org.eclipse.cdt.dsf.gdb.IGDBLaunchConfigurationConstants; import org.eclipse.cdt.dsf.gdb.IGdbDebugPreferenceConstants; import org.eclipse.cdt.dsf.gdb.actions.IConnect; import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin; import org.eclipse.cdt.dsf.gdb.service.IGDBBackend; +import org.eclipse.cdt.dsf.gdb.service.IGDBSourceLookup; import org.eclipse.cdt.dsf.gdb.service.SessionType; import org.eclipse.cdt.dsf.gdb.service.command.IGDBControl; import org.eclipse.cdt.dsf.mi.service.CSourceLookup; @@ -111,6 +113,7 @@ public class FinalLaunchSequence extends ReflectionSequence { "stepSourceGDBInitFile", //$NON-NLS-1$ "stepSetAutoLoadSharedLibrarySymbols", //$NON-NLS-1$ "stepSetSharedLibraryPaths", //$NON-NLS-1$ + "stepSetSourceSubstitutePath", //$NON-NLS-1$ // -environment-directory with a lot of paths could // make setting breakpoint incredibly slow, which makes @@ -483,6 +486,26 @@ public class FinalLaunchSequence extends ReflectionSequence { sourceLookup.setSourceLookupPath(sourceLookupDmc, locator.getSourceContainers(), requestMonitor); } + /** + * Setup the source substitute paths. + * + * This step tells GDB to to handle all the path re-writing using + * "set substitute-path" + * + * @since 5.0 + */ + @Execute + public void stepSetSourceSubstitutePath(RequestMonitor rm) { + IGDBSourceLookup sourceSubPath = fTracker.getService(IGDBSourceLookup.class); + ICommandControlDMContext context = fCommandControl.getContext(); + if (sourceSubPath == null || !(context instanceof ISourceLookupDMContext)) { + rm.done(); + } else { + ISourceLookupDMContext sourceLookupCtx = (ISourceLookupDMContext) context; + sourceSubPath.initializeSourceSubstitutions(sourceLookupCtx, rm); + } + } + private static final String INVALID = "invalid"; //$NON-NLS-1$ /** * If we are dealing with a remote-attach debugging session, connect to the target. diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/GdbLaunchDelegate.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/GdbLaunchDelegate.java index 42a78b94f62..cd37ff473f4 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/GdbLaunchDelegate.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/GdbLaunchDelegate.java @@ -430,7 +430,7 @@ public class GdbLaunchDelegate extends AbstractCLaunchDelegate2 * @since 4.1 */ protected DsfSourceLookupDirector createDsfSourceLocator(ILaunchConfiguration configuration, DsfSession session) throws CoreException { - return new DsfSourceLookupDirector(session); + return new GdbSourceLookupDirector(session); } /** diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/GdbSourceLookupDirector.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/GdbSourceLookupDirector.java new file mode 100644 index 00000000000..4a4bad1d064 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/GdbSourceLookupDirector.java @@ -0,0 +1,175 @@ +/******************************************************************************* + * Copyright (c) 2015 Kichwa Coders 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: + * Jonah Graham (Kichwa Coders) - initial API and implementation to Add support for gdb's "set substitute-path" (Bug 472765) + *******************************************************************************/ +package org.eclipse.cdt.dsf.gdb.launching; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.cdt.debug.core.sourcelookup.IMappingSourceContainer; +import org.eclipse.cdt.debug.core.sourcelookup.MappingSourceContainer; +import org.eclipse.cdt.debug.internal.core.sourcelookup.MapEntrySourceContainer; +import org.eclipse.cdt.dsf.debug.sourcelookup.DsfSourceLookupDirector; +import org.eclipse.cdt.dsf.gdb.service.IGDBSourceLookup; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.Path; +import org.eclipse.debug.core.sourcelookup.ISourceContainer; +import org.eclipse.debug.core.sourcelookup.ISourceLookupParticipant; +import org.eclipse.debug.core.sourcelookup.containers.DefaultSourceContainer; + +/** + * A Source Lookup director that extends the standard DSF version to support the + * GDB backend handling path substitutions using gdb's "set substitute-path" + * mechanism. This director works in tandem with {@link IGDBSourceLookup} + * service to synchronise GDB's path substitutions. + * + * @since 5.0 + */ +public class GdbSourceLookupDirector extends DsfSourceLookupDirector { + private final DsfSession fSession; + + public GdbSourceLookupDirector(DsfSession session) { + super(session); + fSession = session; + } + + @Override + public void initializeParticipants() { + /* + * We don't call super as we don't want DsfSourceLookupParticipant to be + * added explicitly. Instead we use GdbSourceLookupParticipant which + * extends DsfSourceLookupParticipant and does not use the mappings + * directly but relies on GDB's "set substitute-path". + */ + addParticipants(new ISourceLookupParticipant[] { new GdbSourceLookupParticipant(fSession) }); + } + + /** + * Return a map of substitutions with the Key being the compilation path and + * the Value being the machine local path. + * + * @return map of substitutions + */ + public Map getSubstitutionsPaths() { + Map entries = new HashMap<>(); + collectSubstitutionsPaths(getSourceContainers(), entries); + return entries; + } + + protected void collectSubstitutionsPaths(ISourceContainer[] containers, Map entries) { + for (ISourceContainer container : containers) { + if (container instanceof MapEntrySourceContainer) { + MapEntrySourceContainer sourceSubContainer = (MapEntrySourceContainer) container; + + IPath from = sourceSubContainer.getBackendPath(); + IPath to = sourceSubContainer.getLocalPath(); + if (from != null && to != null) { + entries.put(from.toOSString(), to.toOSString()); + } + } else if (container.isComposite()) { + ISourceContainer[] childContainers; + try { + childContainers = container.getSourceContainers(); + } catch (CoreException e) { + /* + * Consistent with other uses of getSourceContainers, we + * silently ignore these children. + */ + childContainers = new ISourceContainer[0]; + } + if (container instanceof MappingSourceContainer) { + MappingSourceContainer mappingSourceContainer = (MappingSourceContainer) container; + if (mappingSourceContainer.isMappingWithBackendEnabled()) { + collectSubstitutionsPaths(childContainers, entries); + } else { + /* + * This mapping has explicitly requested *not* to do a + * substitute, so don't recurse on children here. + */ + } + } else { + /* + * There can be MappingSourceContainers in + * DefaultSourceContainer, but not in other types of + * composite containers (e.g. a DirectorySourceContainer + * cannot contain a MappingSourceContainer). + * + * It is important we don't recurse across all composites + * containers for performance reasons. If a + * DirectorySourceContainer was recursed here, then it could + * means recursing through the entire directory structure + * under that container. + */ + if (container instanceof DefaultSourceContainer) { + collectSubstitutionsPaths(childContainers, entries); + } + } + } + } + } + + /** + * Get the compilation path for the given sourceName. Unlike super's + * version, for {@link MappingSourceContainer}s where backend mapping is + * enabled this method is a no-op as in those cases the backend already + * matches. + */ + @Override + public IPath getCompilationPath(String sourceName) { + return getCompilationPath(getSourceContainers(), sourceName); + } + + /** + * This method mirrors the logic of + * {@link #collectSubstitutionsPaths(ISourceContainer[], Map)} on which + * containers are iterated and excluded. + */ + protected IPath getCompilationPath(ISourceContainer[] containers, String sourceName) { + for (ISourceContainer container : containers) { + + if (container instanceof IMappingSourceContainer) { + IPath mappedPath = ((IMappingSourceContainer) container).getCompilationPath(sourceName); + if (mappedPath != null) { + if (container instanceof MappingSourceContainer + && ((MappingSourceContainer) container).isMappingWithBackendEnabled()) { + /* + * This mapping is being handled by GDB backend (i.e. it was + * collected by collectSubstitutionsPaths to pass to gdb's + * "set substitute-path"). Because GDB is doing the + * translation on it, pass the local name to GDB, not the + * translated name. + */ + return new Path(sourceName); + } + return mappedPath; + } + } else if (container.isComposite()) { + ISourceContainer[] childContainers = null; + try { + childContainers = container.getSourceContainers(); + } catch (CoreException e) { + /* + * Consistent with other uses of getSourceContainers, we + * silently ignore these children. + */ + } + if (childContainers != null) { + IPath path = getCompilationPath(childContainers, sourceName); + if (path != null) { + return path; + } + } + } + } + return null; + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/GdbSourceLookupParticipant.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/GdbSourceLookupParticipant.java new file mode 100644 index 00000000000..f13141ef560 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/GdbSourceLookupParticipant.java @@ -0,0 +1,133 @@ +package org.eclipse.cdt.dsf.gdb.launching; + +import java.util.concurrent.ExecutionException; + +import org.eclipse.cdt.dsf.concurrent.ConfinedToDsfExecutor; +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.DsfExecutor; +import org.eclipse.cdt.dsf.concurrent.Query; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.concurrent.ThreadSafe; +import org.eclipse.cdt.dsf.debug.service.ICachingService; +import org.eclipse.cdt.dsf.debug.service.ISourceLookup.ISourceLookupDMContext; +import org.eclipse.cdt.dsf.debug.service.IStack; +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService; +import org.eclipse.cdt.dsf.debug.sourcelookup.DsfSourceLookupParticipant; +import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin; +import org.eclipse.cdt.dsf.gdb.service.IGDBSourceLookup; +import org.eclipse.cdt.dsf.service.DsfServicesTracker; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.debug.core.sourcelookup.ISourceLookupDirector; + +/** + * Source Lookup Participant that notifies the {@link IGDBSourceLookup} service + * of changes to the lookup path to allow the source lookup service to update + * GDB's substituted paths. + * + * @since 5.0 + */ +@ThreadSafe +public class GdbSourceLookupParticipant extends DsfSourceLookupParticipant { + + private DsfExecutor fExecutor; + private String fSessionId; + private DsfServicesTracker fServicesTracker; + + public GdbSourceLookupParticipant(DsfSession session) { + super(session); + fSessionId = session.getId(); + fExecutor = session.getExecutor(); + fServicesTracker = new DsfServicesTracker(GdbPlugin.getBundleContext(), fSessionId); + } + + @Override + public void init(ISourceLookupDirector director) { + super.init(director); + } + + @Override + public void dispose() { + fServicesTracker.dispose(); + super.dispose(); + } + + @Override + public void sourceContainersChanged(final ISourceLookupDirector director) { + super.sourceContainersChanged(director); + + /* + * Update the substitution paths in GDB. Ideally we would always run + * this atomically to block the source lookup from attempting to do a + * lookup until GDB is fully updated. + * + * However, if we are already on the executor thread there is no way to + * make this atomic. In practice this case does not matter as the times + * sourceContainersChanged is called when on the executor thread are + * when the launch configuration is being saved and in that case the + * source containers are not even changing. + */ + if (fExecutor.isInExecutorThread()) { + sourceContainersChangedOnDispatchThread(director, new RequestMonitor(fExecutor, null)); + } else { + Query query = new Query() { + @Override + protected void execute(final DataRequestMonitor rm) { + sourceContainersChangedOnDispatchThread(director, rm); + } + + }; + fExecutor.execute(query); + try { + query.get(); + } catch (InterruptedException | ExecutionException e) { + // We have failed to update in some way, we don't really have a + // path to expose the failure so at least log it. + GdbPlugin.log(e); + } + } + } + + @ConfinedToDsfExecutor("fExecutor") + protected void sourceContainersChangedOnDispatchThread(final ISourceLookupDirector director, + final RequestMonitor rm) { + IGDBSourceLookup lookup = fServicesTracker.getService(IGDBSourceLookup.class); + if (lookup != null) { + ICommandControlService command = fServicesTracker.getService(ICommandControlService.class); + ISourceLookupDMContext context = (ISourceLookupDMContext) command.getContext(); + lookup.sourceContainersChanged(context, new DataRequestMonitor(fExecutor, rm) { + @Override + protected void handleCompleted() { + /* + * Once GDB is updated, we need to flush the IStack's cache, + * so that #getSourceName get the new names. + */ + if (isSuccess() && getData()) { + IStack stackService = fServicesTracker.getService(IStack.class); + if (stackService instanceof ICachingService) { + /* + * XXX: Ideally we would issue an event here to + * flush the cache. But if we did that we would have + * to add some interlocking to prevent the call to + * IStack.getFrameData() (Called from + * super.getSourceNameOnDispatchThread) from + * returning until the event had been fully + * propogated. At the moment there is no way to + * ensure that happens. + * + * XXX: Adding an event would allow other interested + * parties (such as the Debug View) from being + * notified that the frame data has changed. See Bug + * 489607. + */ + ICachingService cachingStackService = (ICachingService) stackService; + cachingStackService.flushCache(null); + } + } + super.handleCompleted(); + } + }); + } else { + rm.done(); + } + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBSourceLookup.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBSourceLookup.java new file mode 100644 index 00000000000..7115962e40f --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBSourceLookup.java @@ -0,0 +1,158 @@ +/******************************************************************************* + * Copyright (c) 2015 Kichwa Coders 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: + * Jonah Graham (Kichwa Coders) - initial API and implementation to Add support for gdb's "set substitute-path" (Bug 472765) + *******************************************************************************/ +package org.eclipse.cdt.dsf.gdb.service; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.Map; + +import org.eclipse.cdt.debug.internal.core.sourcelookup.CSourceLookupDirector; +import org.eclipse.cdt.dsf.concurrent.CountingRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants; +import org.eclipse.cdt.dsf.concurrent.ImmediateRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.debug.service.command.ICommandControl; +import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin; +import org.eclipse.cdt.dsf.gdb.launching.GdbSourceLookupDirector; +import org.eclipse.cdt.dsf.mi.service.CSourceLookup; +import org.eclipse.cdt.dsf.mi.service.IMICommandControl; +import org.eclipse.cdt.dsf.mi.service.command.CommandFactory; +import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; + +/** + * Default implementation of {@link IGDBSourceLookup} + * + * @since 5.0 + */ +public class GDBSourceLookup extends CSourceLookup implements IGDBSourceLookup { + + private ICommandControl fCommand; + private CommandFactory fCommandFactory; + private Map fDirectors = new HashMap<>(); + /** + * The current set of path substitutions that have been set on GDB. + */ + private Map fCachedEntries = Collections.emptyMap(); + + public GDBSourceLookup(DsfSession session) { + super(session); + } + + @Override + public void initialize(final RequestMonitor rm) { + super.initialize(new ImmediateRequestMonitor(rm) { + @Override + protected void handleSuccess() { + doInitialize(rm); + } + }); + } + + private void doInitialize(RequestMonitor rm) { + fCommand = getServicesTracker().getService(ICommandControl.class); + fCommandFactory = getServicesTracker().getService(IMICommandControl.class).getCommandFactory(); + + register(new String[] { IGDBSourceLookup.class.getName(), GDBSourceLookup.class.getName() }, + new Hashtable()); + + rm.done(); + } + + @Override + public void shutdown(final RequestMonitor rm) { + unregister(); + super.shutdown(rm); + } + + @Override + public void setSourceLookupDirector(ISourceLookupDMContext ctx, CSourceLookupDirector director) { + fDirectors.put(ctx, director); + super.setSourceLookupDirector(ctx, director); + } + + @Override + public void initializeSourceSubstitutions(final ISourceLookupDMContext sourceLookupCtx, final RequestMonitor rm) { + if (!fDirectors.containsKey(sourceLookupCtx)) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.INVALID_HANDLE, + "No source director configured for given context", null)); //$NON-NLS-1$ ); + rm.done(); + return; + } + setSubstitutePaths(sourceLookupCtx, getSubstitutionsPaths(sourceLookupCtx), rm); + } + + private Map getSubstitutionsPaths(ISourceLookupDMContext sourceLookupCtx) { + CSourceLookupDirector director = fDirectors.get(sourceLookupCtx); + if (director instanceof GdbSourceLookupDirector) { + return ((GdbSourceLookupDirector) director).getSubstitutionsPaths(); + } + return Collections.emptyMap(); + } + + @Override + public void sourceContainersChanged(final ISourceLookupDMContext sourceLookupCtx, + final DataRequestMonitor rm) { + if (!fDirectors.containsKey(sourceLookupCtx)) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.INVALID_HANDLE, + "No source director configured for given context", null)); //$NON-NLS-1$ ); + rm.done(); + return; + } + + Map entries = getSubstitutionsPaths(sourceLookupCtx); + if (entries.equals(fCachedEntries)) { + rm.done(false); + } else { + fCommand.queueCommand(fCommandFactory.createCLIUnsetSubstitutePath(sourceLookupCtx), + new DataRequestMonitor(getExecutor(), rm) { + @Override + protected void handleSuccess() { + initializeSourceSubstitutions(sourceLookupCtx, new RequestMonitor(getExecutor(), rm) { + @Override + protected void handleSuccess() { + rm.done(true); + } + }); + } + }); + } + } + + protected void setSubstitutePaths(ISourceLookupDMContext sourceLookupCtx, Map entries, + RequestMonitor rm) { + fCachedEntries = entries; + CountingRequestMonitor countingRm = new CountingRequestMonitor(getExecutor(), rm) { + @Override + protected void handleFailure() { + /* + * We failed to apply the changes. Clear the cache as it does + * not represent the state of the backend. However we don't have + * a good recovery here, so on future sourceContainersChanged() + * calls we will simply reissue the substitutions. + */ + fCachedEntries = null; + rm.done(); + } + }; + countingRm.setDoneCount(entries.size()); + for (Map.Entry entry : entries.entrySet()) { + fCommand.queueCommand( + fCommandFactory.createMISetSubstitutePath(sourceLookupCtx, entry.getKey(), entry.getValue()), + new DataRequestMonitor(getExecutor(), countingRm)); + } + } + +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GdbDebugServicesFactory.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GdbDebugServicesFactory.java index 02e2db9ffa6..ae9edbb3bd1 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GdbDebugServicesFactory.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GdbDebugServicesFactory.java @@ -46,7 +46,6 @@ import org.eclipse.cdt.dsf.gdb.service.command.GDBControl_7_0; import org.eclipse.cdt.dsf.gdb.service.command.GDBControl_7_2; import org.eclipse.cdt.dsf.gdb.service.command.GDBControl_7_4; import org.eclipse.cdt.dsf.gdb.service.command.GDBControl_7_7; -import org.eclipse.cdt.dsf.mi.service.CSourceLookup; import org.eclipse.cdt.dsf.mi.service.IMIBackend; import org.eclipse.cdt.dsf.mi.service.IMIExpressions; import org.eclipse.cdt.dsf.mi.service.MIBreakpoints; @@ -275,7 +274,7 @@ public class GdbDebugServicesFactory extends AbstractDsfDebugServicesFactory { @Override protected ISourceLookup createSourceLookupService(DsfSession session) { - return new CSourceLookup(session); + return new GDBSourceLookup(session); } @Override diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/IGDBSourceLookup.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/IGDBSourceLookup.java new file mode 100644 index 00000000000..2b4fe2da8ce --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/IGDBSourceLookup.java @@ -0,0 +1,46 @@ +/******************************************************************************* + * Copyright (c) 2015 Kichwa Coders 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: + * Jonah Graham (Kichwa Coders) - initial API and implementation to Add support for gdb's "set substitute-path" (Bug 472765) + *******************************************************************************/ +package org.eclipse.cdt.dsf.gdb.service; + +import org.eclipse.cdt.debug.core.sourcelookup.MappingSourceContainer; +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.debug.service.ISourceLookup; + +/** + * Extension to the {@link ISourceLookup} service that allows GDB backend to + * handle the path mapping source container type - + * {@link MappingSourceContainer} - on the GDB side using GDB's + * "set substitute-path" mechanism. + * + * @since 5.0 + */ +public interface IGDBSourceLookup extends ISourceLookup { + + /** + * Initialise the source substitutions on the GDB backend (aka do the + * initial "set substitute-path"s) + * + * @param sourceLookupCtx + * @param rm + */ + void initializeSourceSubstitutions(ISourceLookupDMContext sourceLookupCtx, RequestMonitor rm); + + /** + * Update the source substitutions on the GDB backend (aka modify the + * "set substitute-path"s) + * + * @param sourceLookupCtx + * @param rm + * with the result set to True if a change was made + */ + void sourceContainersChanged(ISourceLookupDMContext sourceLookupCtx, DataRequestMonitor rm); +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/extensions/GDBSourceLookup_HEAD.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/extensions/GDBSourceLookup_HEAD.java index a0acd1dbb39..106913a0475 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/extensions/GDBSourceLookup_HEAD.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/extensions/GDBSourceLookup_HEAD.java @@ -8,8 +8,8 @@ package org.eclipse.cdt.dsf.gdb.service.extensions; import org.eclipse.cdt.dsf.debug.service.ISourceLookup; +import org.eclipse.cdt.dsf.gdb.service.GDBSourceLookup; import org.eclipse.cdt.dsf.gdb.service.GdbDebugServicesFactory; -import org.eclipse.cdt.dsf.mi.service.CSourceLookup; import org.eclipse.cdt.dsf.service.DsfSession; /** @@ -36,7 +36,7 @@ import org.eclipse.cdt.dsf.service.DsfSession; * * @since 4.8 */ -public class GDBSourceLookup_HEAD extends CSourceLookup { +public class GDBSourceLookup_HEAD extends GDBSourceLookup { public GDBSourceLookup_HEAD(DsfSession session) { super(session); diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/CommandFactory.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/CommandFactory.java index 0c5c5324719..01a6057e615 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/CommandFactory.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/CommandFactory.java @@ -40,6 +40,7 @@ import org.eclipse.cdt.dsf.debug.service.IModules.IModuleDMContext; import org.eclipse.cdt.dsf.debug.service.IModules.ISymbolDMContext; 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.ISourceLookup.ISourceLookupDMContext; import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMContext; import org.eclipse.cdt.dsf.debug.service.command.ICommand; import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlDMContext; @@ -69,6 +70,7 @@ import org.eclipse.cdt.dsf.mi.service.command.commands.CLIThread; import org.eclipse.cdt.dsf.mi.service.command.commands.CLITrace; import org.eclipse.cdt.dsf.mi.service.command.commands.CLITraceDump; import org.eclipse.cdt.dsf.mi.service.command.commands.CLIUnsetEnv; +import org.eclipse.cdt.dsf.mi.service.command.commands.CLIUnsetSubstitutePath; import org.eclipse.cdt.dsf.mi.service.command.commands.MIAddInferior; import org.eclipse.cdt.dsf.mi.service.command.commands.MIBreakAfter; import org.eclipse.cdt.dsf.mi.service.command.commands.MIBreakCommands; @@ -150,6 +152,7 @@ import org.eclipse.cdt.dsf.mi.service.command.commands.MIInterpreterExecConsoleK import org.eclipse.cdt.dsf.mi.service.command.commands.MIListFeatures; import org.eclipse.cdt.dsf.mi.service.command.commands.MIListThreadGroups; import org.eclipse.cdt.dsf.mi.service.command.commands.MIRemoveInferior; +import org.eclipse.cdt.dsf.mi.service.command.commands.MISetSubstitutePath; import org.eclipse.cdt.dsf.mi.service.command.commands.MIStackInfoDepth; import org.eclipse.cdt.dsf.mi.service.command.commands.MIStackListArguments; import org.eclipse.cdt.dsf.mi.service.command.commands.MIStackListFrames; @@ -380,6 +383,11 @@ public class CommandFactory { return new CLIUnsetEnv(ctx, name); } + /** @since 5.0 */ + public ICommand createCLIUnsetSubstitutePath(ISourceLookupDMContext ctx) { + return new CLIUnsetSubstitutePath(ctx); + } + /** @since 4.0 */ public ICommand createMIAddInferior(ICommandControlDMContext ctx) { return new MIAddInferior(ctx); @@ -904,6 +912,11 @@ public class CommandFactory { return new MIRemoveInferior(ctx, groupId); } + /** @since 5.0 */ + public ICommand createMISetSubstitutePath(ISourceLookupDMContext context, String from, String to) { + return new MISetSubstitutePath(context, from, to); + } + public ICommand createMIStackInfoDepth(IMIExecutionDMContext ctx) { return new MIStackInfoDepth(ctx); } diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/CLIUnsetSubstitutePath.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/CLIUnsetSubstitutePath.java new file mode 100644 index 00000000000..95385f99503 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/CLIUnsetSubstitutePath.java @@ -0,0 +1,28 @@ +/******************************************************************************* + * Copyright (c) 2015 Kichwa Coders 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: + * Jonah Graham (Kichwa Coders) - initial API and implementation to Add support for gdb's "set substitute-path" (Bug 472765) + *******************************************************************************/ +package org.eclipse.cdt.dsf.mi.service.command.commands; + +import org.eclipse.cdt.dsf.debug.service.ISourceLookup.ISourceLookupDMContext; +import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo; + +/** + * unset substitute-path + * + * Deletes all the path substitutions. + * + * @since 5.0 + */ +public class CLIUnsetSubstitutePath extends MIInterpreterExecConsole { + + public CLIUnsetSubstitutePath(ISourceLookupDMContext ctx) { + super(ctx, "unset substitute-path"); //$NON-NLS-1$ + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MISetSubstitutePath.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MISetSubstitutePath.java new file mode 100644 index 00000000000..bdfb71fca93 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MISetSubstitutePath.java @@ -0,0 +1,26 @@ +/******************************************************************************* + * Copyright (c) 2015 Kichwa Coders 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: + * Jonah Graham (Kichwa Coders) - initial API and implementation to Add support for gdb's "set substitute-path" (Bug 472765) + *******************************************************************************/ +package org.eclipse.cdt.dsf.mi.service.command.commands; + +import org.eclipse.cdt.dsf.debug.service.ISourceLookup.ISourceLookupDMContext; + +/** + * -gdb-set substitute-path from to + * + * @since 5.0 + */ +public class MISetSubstitutePath extends MIGDBSet { + + public MISetSubstitutePath(ISourceLookupDMContext context, String from, String to) { + super(context, new String[] { "substitute-path", from, to }); //$NON-NLS-1$ + } + +} diff --git a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/SourceLookupTest.java b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/SourceLookupTest.java index baf571fbc24..765f86bdc8c 100644 --- a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/SourceLookupTest.java +++ b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/SourceLookupTest.java @@ -41,12 +41,19 @@ import org.eclipse.cdt.debug.core.sourcelookup.MappingSourceContainer; import org.eclipse.cdt.debug.core.sourcelookup.ProgramRelativePathSourceContainer; import org.eclipse.cdt.debug.internal.core.sourcelookup.CSourceLookupDirector; import org.eclipse.cdt.debug.internal.core.sourcelookup.MapEntrySourceContainer; +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.Query; import org.eclipse.cdt.dsf.datamodel.IDMContext; import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMContext; import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMData; import org.eclipse.cdt.dsf.debug.sourcelookup.DsfSourceLookupDirector; -import org.eclipse.cdt.tests.dsf.gdb.framework.AsyncCompletionWaitor; +import org.eclipse.cdt.dsf.gdb.service.command.IGDBControl; +import org.eclipse.cdt.dsf.mi.service.command.CommandFactory; +import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo; import org.eclipse.cdt.dsf.mi.service.command.output.MIMixedInstruction; +import org.eclipse.cdt.dsf.service.DsfServicesTracker; +import org.eclipse.cdt.dsf.service.DsfSession; +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.SyncUtil; @@ -185,6 +192,9 @@ public class SourceLookupTest extends BaseTestCase { } }; + private IGDBControl fCommandControl; + private CommandFactory fCommandFactory; + @Override public void doBeforeTest() throws Exception { removeTeminatedLaunchesBeforeTest(); @@ -213,6 +223,19 @@ public class SourceLookupTest extends BaseTestCase { protected void doLaunch(String programName) throws Exception { setLaunchAttribute(ICDTLaunchConfigurationConstants.ATTR_PROGRAM_NAME, programName); super.doLaunch(); + + final DsfSession session = getGDBLaunch().getSession(); + Runnable runnable = new Runnable() { + + @Override + public void run() { + DsfServicesTracker tracker = new DsfServicesTracker(TestsPlugin.getBundleContext(), session.getId()); + fCommandControl = tracker.getService(IGDBControl.class); + fCommandFactory = fCommandControl.getCommandFactory(); + tracker.dispose(); + } + }; + session.getExecutor().submit(runnable).get(); } @Override @@ -234,16 +257,16 @@ public class SourceLookupTest extends BaseTestCase { * file. */ protected void assertSourceNotFound() throws Throwable { - // Check file name as returned from back end - IFrameDMData frameData = SyncUtil.getFrameData(0, 0); - assertFalse("GDB Unexpectedly located the source", fileExists(frameData.getFile())); - // Check file as resolved by source lookup director ISourceLookupDirector director = (ISourceLookupDirector) getGDBLaunch().getSourceLocator(); IFrameDMContext frameDmc = SyncUtil.getStackFrame(0, 0); Object sourceElement = director.getSourceElement(frameDmc); assertNull("Source Locator unexpectedly found the source", sourceElement); + // Check file name as returned from back end + IFrameDMData frameData = SyncUtil.getFrameData(0, 0); + assertFalse("GDB Unexpectedly located the source", fileExists(frameData.getFile())); + // Check file as resolved by ISourceLookup service try { SyncUtil.getSource(frameData.getFile()); @@ -260,16 +283,16 @@ public class SourceLookupTest extends BaseTestCase { * found the source file. */ protected void assertSourceFoundByDirectorOnly() throws Throwable { - // Check file name as returned from back end - IFrameDMData frameData = SyncUtil.getFrameData(0, 0); - assertFalse("GDB Unexpectedly located the source", fileExists(frameData.getFile())); - // Check file as resolved by source lookup director ISourceLookupDirector director = (ISourceLookupDirector) getGDBLaunch().getSourceLocator(); IFrameDMContext frameDmc = SyncUtil.getStackFrame(0, 0); Object sourceElement = director.getSourceElement(frameDmc); assertTrue("Source locator failed to find source", sourceElement instanceof IStorage); + // Check file name as returned from back end + IFrameDMData frameData = SyncUtil.getFrameData(0, 0); + assertFalse("GDB Unexpectedly located the source", fileExists(frameData.getFile())); + // Check file as resolved by ISourceLookup service sourceElement = SyncUtil.getSource(frameData.getFile()); assertTrue("Source Lookup service failed to find source", sourceElement instanceof IStorage); @@ -279,16 +302,16 @@ public class SourceLookupTest extends BaseTestCase { * Custom assertion that GDB and the Source Locator found the source file. */ protected void assertSourceFound() throws Throwable { - // Check file name as returned from back end - IFrameDMData frameData = SyncUtil.getFrameData(0, 0); - assertTrue("GDB failed to find source", fileExists(frameData.getFile())); - // Check file as resolved by source lookup director ISourceLookupDirector director = (ISourceLookupDirector) getGDBLaunch().getSourceLocator(); IFrameDMContext frameDmc = SyncUtil.getStackFrame(0, 0); Object sourceElement = director.getSourceElement(frameDmc); assertTrue("Source locator failed to find source", sourceElement instanceof IStorage); + // Check file name as returned from back end + IFrameDMData frameData = SyncUtil.getFrameData(0, 0); + assertTrue("GDB failed to find source", fileExists(frameData.getFile())); + // Check file as resolved by ISourceLookup service sourceElement = SyncUtil.getSource(frameData.getFile()); assertTrue("Source Lookup service failed to find source", sourceElement instanceof IStorage); @@ -328,10 +351,11 @@ public class SourceLookupTest extends BaseTestCase { /** * Add the mapping source container to the common source lookup */ - protected void doMappingInCommon(boolean canonical) { + protected void doMappingInCommon(boolean canonical, boolean withBackend) { CSourceLookupDirector commonSourceLookupDirector = CDebugCorePlugin.getDefault() .getCommonSourceLookupDirector(); MappingSourceContainer mapContainer = new MappingSourceContainer("Mappings"); + mapContainer.setIsMappingWithBackendEnabled(withBackend); if (canonical) { mapContainer.addMapEntry(fMapEntrySourceContainerC); } else { @@ -362,11 +386,10 @@ public class SourceLookupTest extends BaseTestCase { * Set default source locators and a path mapping * {@link MappingSourceContainer} from BUILD_ABSPATH -> SOURCE_ABSPATH and * do the launch - * - * @return */ - protected void doMappingAndLaunch(String programName) throws CoreException, Exception { + protected void doMappingAndLaunch(String programName, boolean withBackend) throws CoreException, Exception { MappingSourceContainer mapContainer = new MappingSourceContainer("Mappings"); + mapContainer.setIsMappingWithBackendEnabled(withBackend); if (programName.endsWith("N.exe")) { mapContainer.addMapEntry(fMapEntrySourceContainerN); } else if (programName.endsWith("C.exe")) { @@ -379,20 +402,39 @@ public class SourceLookupTest extends BaseTestCase { } /** - * With mapping test that GDB does not locate the file, but the source - * lookup director and the source lookup service do find the file. + * Mapping test common. + * + * If backend is used for mapping then every layer should be able to find + * source. + * + * If backned is not used for mapping then only once the source lookup + * director gets involved should the source be found as GDB will not know + * how to find it on its own. */ - protected void sourceMapping(String programName) throws Throwable { - doMappingAndLaunch(programName); - assertSourceFoundByDirectorOnly(); + protected void sourceMapping(String programName, boolean withBackend) throws Throwable { + doMappingAndLaunch(programName, withBackend); + if (withBackend) { + assertSourceFound(); + } else { + assertSourceFoundByDirectorOnly(); + } } /** * With mapping test breakpoints can be inserted. */ - protected void sourceMappingBreakpoints(String programName) throws Throwable { - doMappingAndLaunch(programName); + protected void sourceMappingBreakpoints(String programName, boolean withBackend) throws Throwable { + doMappingAndLaunch(programName, withBackend); + assertInsertBreakpointSuccessful(); + } + + /** + * Assert that a breakpoint can be successfully inserted. To successfully + * insert a breakpoint it means the the mapping of local file names to + * compilation paths is working properly. + */ + protected void assertInsertBreakpointSuccessful() throws Throwable { // insert breakpoint in source file fBreakpointInstalledWait.waitReset(); ICLineBreakpoint bp = CDIDebugModel.createLineBreakpoint( @@ -414,7 +456,16 @@ public class SourceLookupTest extends BaseTestCase { */ @Test public void sourceMappingAC() throws Throwable { - sourceMapping(EXEC_AC_NAME); + sourceMapping(EXEC_AC_NAME, false); + } + + /** + * Test source mappings with executable built with an Absolute and Canonical + * build path + */ + @Test + public void sourceSubstituteAC() throws Throwable { + sourceMapping(EXEC_AC_NAME, true); } /** @@ -423,7 +474,17 @@ public class SourceLookupTest extends BaseTestCase { */ @Test public void sourceMappingAN() throws Throwable { - sourceMapping(EXEC_AN_NAME); + sourceMapping(EXEC_AN_NAME, false); + } + + /** + * Test source mappings with executable built with an Absolute and + * Non-canonical build path + */ + @Test + @Ignore("Not supported because GDB does not handle non-canonical paths. See Bug 477057") + public void sourceSubstituteAN() throws Throwable { + sourceMapping(EXEC_AN_NAME, true); } /** @@ -432,7 +493,16 @@ public class SourceLookupTest extends BaseTestCase { */ @Test public void sourceMappingRC() throws Throwable { - sourceMapping(EXEC_RC_NAME); + sourceMapping(EXEC_RC_NAME, false); + } + + /** + * Test source mappings with executable built with a Relative and Canonical + * build path + */ + @Test + public void sourceSubstituteRC() throws Throwable { + sourceMapping(EXEC_RC_NAME, true); } /** @@ -441,7 +511,17 @@ public class SourceLookupTest extends BaseTestCase { */ @Test public void sourceMappingRN() throws Throwable { - sourceMapping(EXEC_RN_NAME); + sourceMapping(EXEC_RN_NAME, false); + } + + /** + * Test source mappings with executable built with a Relative and + * Non-canonical build path + */ + @Test + @Ignore("Not supported because GDB does not handle non-canonical paths. See Bug 477057") + public void sourceSubstituteRN() throws Throwable { + sourceMapping(EXEC_RN_NAME, true); } /** @@ -450,7 +530,16 @@ public class SourceLookupTest extends BaseTestCase { */ @Test public void sourceMappingBreakpointsAC() throws Throwable { - sourceMappingBreakpoints(EXEC_AC_NAME); + sourceMappingBreakpoints(EXEC_AC_NAME, false); + } + + /** + * Test source mappings with executable built with an Absolute and Canonical + * build path + */ + @Test + public void sourceSubstituteBreakpointsAC() throws Throwable { + sourceMappingBreakpoints(EXEC_AC_NAME, true); } /** @@ -460,7 +549,17 @@ public class SourceLookupTest extends BaseTestCase { @Ignore("Not supported because GDB does not handle non-canonical paths. See Bug 477057") @Test public void sourceMappingBreakpointsAN() throws Throwable { - sourceMappingBreakpoints(EXEC_AN_NAME); + sourceMappingBreakpoints(EXEC_AN_NAME, false); + } + + /** + * Test source mappings with executable built with an Absolute and + * Non-canonical build path + */ + @Test + @Ignore("Not supported because GDB does not handle non-canonical paths. See Bug 477057") + public void sourceSubstituteBreakpointsAN() throws Throwable { + sourceMappingBreakpoints(EXEC_AN_NAME, true); } /** @@ -469,7 +568,16 @@ public class SourceLookupTest extends BaseTestCase { */ @Test public void sourceMappingBreakpointsRC() throws Throwable { - sourceMappingBreakpoints(EXEC_RC_NAME); + sourceMappingBreakpoints(EXEC_RC_NAME, false); + } + + /** + * Test source mappings with executable built with a Relative and Canonical + * build path + */ + @Test + public void sourceSubstituteBreakpointsRC() throws Throwable { + sourceMappingBreakpoints(EXEC_RC_NAME, true); } /** @@ -479,7 +587,33 @@ public class SourceLookupTest extends BaseTestCase { @Ignore("Not supported because GDB does not handle non-canonical paths. See Bug 477057") @Test public void sourceMappingBreakpointsRN() throws Throwable { - sourceMappingBreakpoints(EXEC_RN_NAME); + sourceMappingBreakpoints(EXEC_RN_NAME, false); + } + + /** + * Test source mappings with executable built with a Relative and + * Non-canonical build path + */ + @Test + @Ignore("Not supported because GDB does not handle non-canonical paths. See Bug 477057") + public void sourceSubstituteBreakpointsRN() throws Throwable { + sourceMappingBreakpoints(EXEC_RN_NAME, true); + } + + /** + * Change directory to the binary (aka EXEC_PATH) + */ + protected void doCdToBinDir() throws Exception { + Query query = new Query() { + @Override + protected void execute(DataRequestMonitor rm) { + fCommandControl.queueCommand(fCommandFactory.createMIEnvironmentCD(fCommandControl.getContext(), + new File(EXEC_PATH).getAbsolutePath()), rm); + } + }; + + fCommandControl.getExecutor().execute(query); + query.get(); } /** @@ -487,15 +621,18 @@ public class SourceLookupTest extends BaseTestCase { * debug session (e.g. with CSourceNotFoundEditor) that the lookups are * updated. */ - @Test - public void sourceMappingChanges() throws Throwable { - doMappingAndLaunch(EXEC_AC_NAME); + public void sourceMappingChangesHelper(boolean withBackend) throws Throwable { + doMappingAndLaunch(EXEC_AC_NAME, withBackend); DsfSourceLookupDirector sourceLocator = (DsfSourceLookupDirector) getGDBLaunch().getSourceLocator(); MapEntrySourceContainer incorrectMapEntry = new MapEntrySourceContainer( new Path(BUILD_ABSPATH + "/incorrectsubpath"), new Path(SOURCE_ABSPATH)); - assertSourceFoundByDirectorOnly(); + if (withBackend) { + assertSourceFound(); + } else { + assertSourceFoundByDirectorOnly(); + } // Change the source mappings ISourceContainer[] containers = sourceLocator.getSourceContainers(); @@ -504,6 +641,18 @@ public class SourceLookupTest extends BaseTestCase { mappingSourceContainer.addMapEntry(incorrectMapEntry); sourceLocator.setSourceContainers(containers); + /* + * GDB (pre 7.0) changes the current directory when the above source is + * found. As a result GDB is able to find the source even though we have + * changed the source lookup paths. To make sure that GDB is really + * doing a substitution rather than looking in current directory, change + * the current directory. Without this, the assertSourceNotFound fails + * because GDB unexpectedly finds the source (for the wrong reason). + */ + if (withBackend) { + doCdToBinDir(); + } + assertSourceNotFound(); // Change the source mappings back @@ -513,7 +662,21 @@ public class SourceLookupTest extends BaseTestCase { mappingSourceContainer.addMapEntry(fMapEntrySourceContainerC); sourceLocator.setSourceContainers(containers); - assertSourceFoundByDirectorOnly(); + if (withBackend) { + assertSourceFound(); + } else { + assertSourceFoundByDirectorOnly(); + } + } + + @Test + public void sourceMappingChanges() throws Throwable { + sourceMappingChangesHelper(false); + } + + @Test + public void sourceSubstituteChanges() throws Throwable { + sourceMappingChangesHelper(true); } /** @@ -524,8 +687,7 @@ public class SourceLookupTest extends BaseTestCase { * This version is for a new source mapping where there wasn't one * previously. */ - @Test - public void sourceMappingAdded() throws Throwable { + public void sourceMappingAddedHelper(boolean withBackend) throws Throwable { doLaunch(EXEC_PATH + EXEC_AC_NAME); assertSourceNotFound(); @@ -534,13 +696,28 @@ public class SourceLookupTest extends BaseTestCase { DsfSourceLookupDirector sourceLocator = (DsfSourceLookupDirector) getGDBLaunch().getSourceLocator(); ISourceContainer[] containers = sourceLocator.getSourceContainers(); MappingSourceContainer mappingSourceContainer = new MappingSourceContainer("Mappings"); + mappingSourceContainer.setIsMappingWithBackendEnabled(withBackend); mappingSourceContainer.addMapEntry(fMapEntrySourceContainerC); ISourceContainer[] newContainers = new ISourceContainer[containers.length + 1]; System.arraycopy(containers, 0, newContainers, 0, containers.length); newContainers[newContainers.length - 1] = mappingSourceContainer; sourceLocator.setSourceContainers(newContainers); - assertSourceFoundByDirectorOnly(); + if (withBackend) { + assertSourceFound(); + } else { + assertSourceFoundByDirectorOnly(); + } + } + + @Test + public void sourceMappingAdded() throws Throwable { + sourceMappingAddedHelper(false); + } + + @Test + public void sourceSubstituteAdded() throws Throwable { + sourceMappingAddedHelper(true); } /** @@ -635,11 +812,20 @@ public class SourceLookupTest extends BaseTestCase { * In this case, the DSF specific director created as part of the launch * gets used. */ + public void sourceFinderMappingAC_ActiveLaunchHelper(boolean withBackend) throws Throwable { + assertFinderDoesNotFind(EXEC_AC_NAME, new File(BUILD_PATH, SOURCE_NAME).getAbsolutePath()); + doMappingAndLaunch(EXEC_AC_NAME, withBackend); + assertFinderFinds(EXEC_AC_NAME, new File(SOURCE_PATH, SOURCE_NAME).getAbsolutePath()); + } + @Test public void sourceFinderMappingAC_ActiveLaunch() throws Throwable { - assertFinderDoesNotFind(EXEC_AC_NAME, new File(BUILD_PATH, SOURCE_NAME).getAbsolutePath()); - doMappingAndLaunch(EXEC_AC_NAME); - assertFinderFinds(EXEC_AC_NAME, new File(SOURCE_PATH, SOURCE_NAME).getAbsolutePath()); + sourceFinderMappingAC_ActiveLaunchHelper(false); + } + + @Test + public void sourceFinderSubstituteAC_ActiveLaunch() throws Throwable { + sourceFinderMappingAC_ActiveLaunchHelper(true); } /** @@ -649,15 +835,24 @@ public class SourceLookupTest extends BaseTestCase { * In this case, the DSF specific director created as part of the launch * gets used. */ - @Test - public void sourceFinderMappingAC_TerminatedLaunch() throws Throwable { - sourceFinderMappingAC_ActiveLaunch(); + public void sourceFinderMappingAC_TerminatedLaunchHelper(boolean withBackend) throws Throwable { + sourceFinderMappingAC_ActiveLaunchHelper(withBackend); // Terminate the launch, but don't remove it doAfterTest(); assertFinderFinds(EXEC_AC_NAME, new File(SOURCE_PATH, SOURCE_NAME).getAbsolutePath()); } + @Test + public void sourceFinderMappingAC_TerminatedLaunch() throws Throwable { + sourceFinderMappingAC_TerminatedLaunchHelper(false); + } + + @Test + public void sourceFinderSubstituteAC_TerminatedLaunch() throws Throwable { + sourceFinderMappingAC_ActiveLaunchHelper(true); + } + /** * Test the CSourceFinder's use of source lookup when there is a not active * launch, but a launch configuration that can be used. @@ -665,9 +860,8 @@ public class SourceLookupTest extends BaseTestCase { * In this case, the c general director created as part of the launch gets * used. */ - @Test - public void sourceFinderMappingAC_LaunchConfig() throws Throwable { - sourceFinderMappingAC_TerminatedLaunch(); + public void sourceFinderMappingAC_LaunchConfigHelper(boolean withBackend) throws Throwable { + sourceFinderMappingAC_TerminatedLaunchHelper(withBackend); // Remove the launch, so that we can test with the existing // configuration @@ -683,15 +877,24 @@ public class SourceLookupTest extends BaseTestCase { assertFinderFinds(EXEC_AC_NAME, new File(SOURCE_PATH, SOURCE_NAME).getAbsolutePath()); } + @Test + public void sourceFinderMappingAC_LaunchConfig() throws Throwable { + sourceFinderMappingAC_LaunchConfigHelper(false); + } + + @Test + public void sourceFinderSubstituteAC_LaunchConfig() throws Throwable { + sourceFinderMappingAC_LaunchConfigHelper(true); + } + /** * Test that CSourceFinder works with the common source director, i.e. no * launches or launch configs in place. */ - @Test - public void sourceFinderMappingAC_CommonLocator() throws Throwable { + public void sourceFinderMappingAC_CommonLocatorHelper(boolean withBackend) throws Throwable { assertFinderDoesNotFind(EXEC_AC_NAME, new File(BUILD_PATH, SOURCE_NAME).getAbsolutePath()); - doMappingInCommon(true); + doMappingInCommon(true, withBackend); try { assertFinderFinds(EXEC_AC_NAME, new File(SOURCE_PATH, SOURCE_NAME).getAbsolutePath()); } finally { @@ -701,6 +904,16 @@ public class SourceLookupTest extends BaseTestCase { assertFinderDoesNotFind(EXEC_AC_NAME, new File(BUILD_PATH, SOURCE_NAME).getAbsolutePath()); } + @Test + public void sourceFinderMappingAC_CommonLocator() throws Throwable { + sourceFinderMappingAC_CommonLocatorHelper(false); + } + + @Test + public void sourceFinderSubstituteAC_CommonLocator() throws Throwable { + sourceFinderMappingAC_CommonLocatorHelper(true); + } + /** * This test verifies that doing a source lookup where the absolute name of * the file is provided by the backend resolves. @@ -733,4 +946,38 @@ public class SourceLookupTest extends BaseTestCase { assertSourceFound(); } + + /** + * Test verifies interaction between director that has two mappers, one with + * backend enabled and one without with the second being the only valid one. + */ + @Test + public void twoMappersSecondValid() throws Throwable { + MappingSourceContainer substituteContainer = new MappingSourceContainer("Mappings With Backend"); + substituteContainer.setIsMappingWithBackendEnabled(true); + /* + * the entry here does not matter, as long as it is not the valid + * substitution, we want to make sure that we process the other + * MappingSourceContainer correctly + */ + substituteContainer + .addMapEntry(new MapEntrySourceContainer(new Path("/from_invalid"), new Path("/to_invalid"))); + AbstractSourceLookupDirector director = setSourceContainer(substituteContainer); + + // this is the mapping we want to do the work + MappingSourceContainer mapContainer = new MappingSourceContainer("Mappings"); + mapContainer.setIsMappingWithBackendEnabled(false); + mapContainer.addMapEntry(fMapEntrySourceContainerC); + addSourceContainer(director, mapContainer); + + doLaunch(EXEC_PATH + EXEC_AC_NAME); + + /* + * because the backend substitution does not apply, we resolve with the + * CDT mapping, in this case that means that only the director should + * locate the source. + */ + assertSourceFoundByDirectorOnly(); + assertInsertBreakpointSuccessful(); + } } diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/launch/QtLocalDebugLaunchConfigDelegate.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/launch/QtLocalDebugLaunchConfigDelegate.java index e360dcd0b35..af7ed6fd443 100644 --- a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/launch/QtLocalDebugLaunchConfigDelegate.java +++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/internal/qt/core/launch/QtLocalDebugLaunchConfigDelegate.java @@ -15,8 +15,8 @@ import org.eclipse.cdt.dsf.concurrent.ImmediateExecutor; import org.eclipse.cdt.dsf.concurrent.Query; import org.eclipse.cdt.dsf.concurrent.RequestMonitorWithProgress; import org.eclipse.cdt.dsf.concurrent.Sequence; -import org.eclipse.cdt.dsf.debug.sourcelookup.DsfSourceLookupDirector; import org.eclipse.cdt.dsf.gdb.launching.GdbLaunch; +import org.eclipse.cdt.dsf.gdb.launching.GdbSourceLookupDirector; import org.eclipse.cdt.dsf.gdb.launching.ServicesLaunchSequence; import org.eclipse.cdt.dsf.gdb.service.GdbDebugServicesFactory; import org.eclipse.cdt.dsf.gdb.service.command.IGDBControl; @@ -43,7 +43,7 @@ public class QtLocalDebugLaunchConfigDelegate extends QtLaunchConfigurationDeleg launch.setLaunchTarget(target); launch.initialize(); - DsfSourceLookupDirector locator = new DsfSourceLookupDirector(launch.getSession()); + GdbSourceLookupDirector locator = new GdbSourceLookupDirector(launch.getSession()); String memento = configuration.getAttribute(ILaunchConfiguration.ATTR_SOURCE_LOCATOR_MEMENTO, (String) null); if (memento == null) { locator.initializeDefaults(configuration);