/******************************************************************************* * Copyright (c) 2015, 2016 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<ISourceLookupDMContext, CSourceLookupDirector> fDirectors = new HashMap<>(); /** * The current set of path substitutions that have been set on GDB. */ private Map<String, String> 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<String, String>()); 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<String, String> 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<Boolean> 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<String, String> entries = getSubstitutionsPaths(sourceLookupCtx); if (entries.equals(fCachedEntries)) { rm.done(false); } else { /* * Issue the clear and set commands back to back so that the * executor thread atomically changes the source lookup settings. * Any commands to GDB issued after this call will get the new * source substitute settings. */ CountingRequestMonitor countingRm = new CountingRequestMonitor(getExecutor(), rm) { @Override protected void handleSuccess() { rm.done(true); } }; fCommand.queueCommand(fCommandFactory.createCLIUnsetSubstitutePath(sourceLookupCtx), new DataRequestMonitor<MIInfo>(getExecutor(), countingRm)); initializeSourceSubstitutions(sourceLookupCtx, new RequestMonitor(getExecutor(), countingRm)); countingRm.setDoneCount(2); } } protected void setSubstitutePaths(ISourceLookupDMContext sourceLookupCtx, Map<String, String> 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<String, String> entry : entries.entrySet()) { fCommand.queueCommand( fCommandFactory.createMISetSubstitutePath(sourceLookupCtx, entry.getKey(), entry.getValue()), new DataRequestMonitor<MIInfo>(getExecutor(), countingRm)); } } }