blob: a76718afb93074fb92bc3d3938e965d09d8d9041 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2006, 2016 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
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Wind River Systems - initial API and implementation
* Marc Khouzam (Ericsson) - Fix NPE for partial launches (Bug 368597)
* Marc Khouzam (Ericsson) - Create the gdb process through the process factory (Bug 210366)
* Alvaro Sanchez-Leon (Ericsson AB) - Each memory context needs a different MemoryRetrieval (Bug 250323)
*******************************************************************************/
package org.eclipse.cdt.dsf.gdb.launching;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.RejectedExecutionException;
import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.cdtvariables.CdtVariableException;
import org.eclipse.cdt.core.cdtvariables.ICdtVariable;
import org.eclipse.cdt.core.cdtvariables.ICdtVariableManager;
import org.eclipse.cdt.core.envvar.IEnvironmentVariable;
import org.eclipse.cdt.core.model.CoreModel;
import org.eclipse.cdt.core.model.ICProject;
import org.eclipse.cdt.core.parser.util.StringUtil;
import org.eclipse.cdt.core.settings.model.ICConfigurationDescription;
import org.eclipse.cdt.core.settings.model.ICProjectDescription;
import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants;
import org.eclipse.cdt.debug.internal.core.CRequest;
import org.eclipse.cdt.dsf.concurrent.ConfinedToDsfExecutor;
import org.eclipse.cdt.dsf.concurrent.DefaultDsfExecutor;
import org.eclipse.cdt.dsf.concurrent.DsfExecutor;
import org.eclipse.cdt.dsf.concurrent.DsfRunnable;
import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants;
import org.eclipse.cdt.dsf.concurrent.ImmediateExecutor;
import org.eclipse.cdt.dsf.concurrent.ImmediateRequestMonitor;
import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
import org.eclipse.cdt.dsf.concurrent.Sequence;
import org.eclipse.cdt.dsf.concurrent.Sequence.Step;
import org.eclipse.cdt.dsf.concurrent.ThreadSafe;
import org.eclipse.cdt.dsf.concurrent.ThreadSafeAndProhibitedFromDsfExecutor;
import org.eclipse.cdt.dsf.debug.internal.provisional.model.IMemoryBlockRetrievalManager;
import org.eclipse.cdt.dsf.debug.model.DsfLaunch;
import org.eclipse.cdt.dsf.debug.service.IDsfDebugServicesFactory;
import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlShutdownDMEvent;
import org.eclipse.cdt.dsf.gdb.IGDBLaunchConfigurationConstants;
import org.eclipse.cdt.dsf.gdb.IGdbDebugConstants;
import org.eclipse.cdt.dsf.gdb.IGdbDebugPreferenceConstants;
import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin;
import org.eclipse.cdt.dsf.gdb.internal.memory.GdbMemoryBlockRetrievalManager;
import org.eclipse.cdt.dsf.gdb.service.command.IGDBControl;
import org.eclipse.cdt.dsf.mi.service.command.AbstractCLIProcess;
import org.eclipse.cdt.dsf.service.DsfServiceEventHandler;
import org.eclipse.cdt.dsf.service.DsfServicesTracker;
import org.eclipse.cdt.dsf.service.DsfSession;
import org.eclipse.cdt.utils.CommandLineUtil;
import org.eclipse.cdt.utils.spawner.ProcessFactory;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.MultiStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.core.variables.VariablesPlugin;
import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
import org.eclipse.debug.core.ILaunchManager;
import org.eclipse.debug.core.IStatusHandler;
import org.eclipse.debug.core.commands.IDebugCommandRequest;
import org.eclipse.debug.core.commands.IDisconnectHandler;
import org.eclipse.debug.core.commands.ITerminateHandler;
import org.eclipse.debug.core.model.IDisconnect;
import org.eclipse.debug.core.model.ISourceLocator;
import org.eclipse.debug.core.model.ITerminate;
import org.eclipse.launchbar.core.target.ILaunchTarget;
import org.eclipse.launchbar.core.target.launch.ITargetedLaunch;
/**
* The only object in the model that implements the traditional interfaces.
*/
@ThreadSafe
public class GdbLaunch extends DsfLaunch implements ITerminate, IDisconnect, ITracedLaunch, ITargetedLaunch {
private DefaultDsfExecutor fExecutor;
private DsfSession fSession;
private DsfServicesTracker fTracker;
private boolean fInitialized = false;
private boolean fShutDown = false;
private IMemoryBlockRetrievalManager fMemRetrievalManager;
private IDsfDebugServicesFactory fServiceFactory;
private ILaunchTarget fLaunchTarget;
public GdbLaunch(ILaunchConfiguration launchConfiguration, String mode, ISourceLocator locator) {
super(launchConfiguration, mode, locator);
// Create the dispatch queue to be used by debugger control and services
// that belong to this launch
final DefaultDsfExecutor dsfExecutor = new DefaultDsfExecutor(GdbLaunchDelegate.GDB_DEBUG_MODEL_ID);
dsfExecutor.prestartCoreThread();
fExecutor = dsfExecutor;
fSession = DsfSession.startSession(fExecutor, GdbLaunchDelegate.GDB_DEBUG_MODEL_ID);
}
public DsfExecutor getDsfExecutor() {
return fExecutor;
}
public IDsfDebugServicesFactory getServiceFactory() {
return fServiceFactory;
}
public void initialize() throws DebugException {
/*
* Registering the launch as an adapter. This ensures that this launch
* will be associated with all DMContexts from this session. We do this
* here because we want to have access to the launch even if we run
* headless, but when we run headless, GdbAdapterFactory is not
* initialized.
*/
fSession.registerModelAdapter(ILaunch.class, this);
Runnable initRunnable = new DsfRunnable() {
@Override
public void run() {
fTracker = new DsfServicesTracker(GdbPlugin.getBundleContext(), fSession.getId());
fSession.addServiceEventListener(GdbLaunch.this, null);
fInitialized = true;
fireChanged();
}
};
// Invoke the execution code and block waiting for the result.
try {
fExecutor.submit(initRunnable).get();
} catch (InterruptedException e) {
throw new DebugException(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.INTERNAL_ERROR,
"Error initializing launch", e)); //$NON-NLS-1$
} catch (ExecutionException e) {
throw new DebugException(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.INTERNAL_ERROR,
"Error initializing launch", e)); //$NON-NLS-1$
}
}
public void initializeControl() throws CoreException {
// Create a memory retrieval manager and register it with the session
// To maintain a mapping of memory contexts to the corresponding memory
// retrieval in this session
try {
fExecutor.submit(new Callable<Object>() {
@Override
public Object call() throws CoreException {
fMemRetrievalManager = new GdbMemoryBlockRetrievalManager(GdbLaunchDelegate.GDB_DEBUG_MODEL_ID,
getLaunchConfiguration(), fSession);
fSession.registerModelAdapter(IMemoryBlockRetrievalManager.class, fMemRetrievalManager);
fSession.addServiceEventListener(fMemRetrievalManager, null);
return null;
}
}).get();
} catch (InterruptedException e) {
throw new CoreException(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, 0,
"Interrupted while waiting for get process callable.", e)); //$NON-NLS-1$
} catch (ExecutionException e) {
throw (CoreException) e.getCause();
} catch (RejectedExecutionException e) {
throw new CoreException(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, 0,
"Debugger shut down before launch was completed.", e)); //$NON-NLS-1$
}
}
public DsfSession getSession() {
return fSession;
}
@ThreadSafeAndProhibitedFromDsfExecutor("getDsfExecutor()")
public void addCLIProcess(String label) throws CoreException {
try {
// Add the CLI process object to the launch.
AbstractCLIProcess cliProc = getDsfExecutor().submit(new Callable<AbstractCLIProcess>() {
@Override
public AbstractCLIProcess call() throws CoreException {
IGDBControl gdb = fTracker.getService(IGDBControl.class);
if (gdb != null) {
return gdb.getCLIProcess();
}
return null;
}
}).get();
// Need to go through DebugPlugin.newProcess so that we can use
// the overrideable process factory to allow others to override.
// First set attribute to specify we want to create the gdb process.
// Bug 210366
Map<String, String> attributes = new HashMap<String, String>();
attributes.put(IGdbDebugConstants.PROCESS_TYPE_CREATION_ATTR,
IGdbDebugConstants.GDB_PROCESS_CREATION_VALUE);
DebugPlugin.newProcess(this, cliProc, label, attributes);
} catch (InterruptedException e) {
throw new CoreException(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, 0,
"Interrupted while waiting for get process callable.", e)); //$NON-NLS-1$
} catch (ExecutionException e) {
throw (CoreException) e.getCause();
} catch (RejectedExecutionException e) {
throw new CoreException(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, 0,
"Debugger shut down before launch was completed.", e)); //$NON-NLS-1$
}
}
public void setServiceFactory(IDsfDebugServicesFactory factory) {
fServiceFactory = factory;
}
///////////////////////////////////////////////////////////////////////////
// IServiceEventListener
@DsfServiceEventHandler
public void eventDispatched(ICommandControlShutdownDMEvent event) {
shutdownSession(new ImmediateRequestMonitor());
}
///////////////////////////////////////////////////////////////////////////
// ITerminate
static class LaunchCommandRequest extends CRequest implements IDebugCommandRequest {
private Object[] elements;
public LaunchCommandRequest(Object[] objects) {
elements = objects;
}
@Override
public Object[] getElements() {
return elements;
}
@Override
public void done() {
IStatus status = getStatus();
if (status != null && !status.isOK()) {
IStatusHandler statusHandler = DebugPlugin.getDefault().getStatusHandler(status);
if (statusHandler != null) {
try {
statusHandler.handleStatus(status, null);
} catch (CoreException ex) {
GdbPlugin.getDefault().getLog().log(ex.getStatus());
}
} else {
GdbPlugin.getDefault().getLog().log(status);
}
}
}
}
@Override
public boolean canTerminate() {
return fInitialized && super.canTerminate();
}
/*
* (non-Javadoc)
*
* @see org.eclipse.debug.core.Launch#terminate()
*/
@Override
public void terminate() throws DebugException {
// Execute asynchronously to avoid potential deadlocks
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=434645
ITerminateHandler handler = getAdapter(ITerminateHandler.class);
if (handler == null) {
super.terminate();
return;
}
LaunchCommandRequest req = new LaunchCommandRequest(new Object[] { this });
handler.execute(req);
}
// ITerminate
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
// IDisconnect
@Override
public boolean canDisconnect() {
return canTerminate();
}
@Override
public boolean isDisconnected() {
return isTerminated();
}
@Override
public void disconnect() throws DebugException {
IDisconnectHandler handler = getAdapter(IDisconnectHandler.class);
if (handler == null) {
super.disconnect();
return;
}
LaunchCommandRequest req = new LaunchCommandRequest(new Object[] { this });
handler.execute(req);
}
// IDisconnect
///////////////////////////////////////////////////////////////////////////
/**
* Terminates the gdb session, shuts down the services, the session and the
* executor associated with this launch.
* <p>
* Note: The argument request monitor to this method should NOT use the
* executor that belongs to this launch. By the time the shutdown is
* complete, this executor will not be dispatching anymore and the request
* monitor will never be invoked. Instead callers should use the
* {@link ImmediateExecutor}.
* </p>
*
* @param rm
* The request monitor invoked when the shutdown is complete.
*/
@ConfinedToDsfExecutor("getSession().getExecutor()")
public void shutdownSession(final RequestMonitor rm) {
if (fShutDown) {
rm.done();
return;
}
fShutDown = true;
final Sequence shutdownSeq = new ShutdownSequence(getDsfExecutor(), fSession.getId(),
new RequestMonitor(fSession.getExecutor(), rm) {
@Override
public void handleCompleted() {
if (fMemRetrievalManager != null) {
fSession.removeServiceEventListener(fMemRetrievalManager);
fMemRetrievalManager.dispose();
}
fSession.removeServiceEventListener(GdbLaunch.this);
if (!isSuccess()) {
GdbPlugin.getDefault().getLog().log(new MultiStatus(GdbPlugin.PLUGIN_ID, -1,
new IStatus[] { getStatus() }, "Session shutdown failed", null)); //$NON-NLS-1$
}
// Last order of business, shutdown the dispatch queue.
if (fTracker != null) {
fTracker.dispose();
fTracker = null;
}
DsfSession.endSession(fSession);
// 'fireTerminate()' removes this launch from the list
// of 'DebugEvent'
// listeners. The launch may not be terminated at this
// point: the inferior
// and gdb processes are monitored in separate threads.
// This will prevent
// updating of some of the Debug view actions.
// 'DebugEvent.TERMINATE' will be fired when each of the
// corresponding processes
// exits and handled by 'handleDebugEvents()' method.
if (isTerminated()) {
fireTerminate();
}
rm.setStatus(getStatus());
rm.done();
}
});
final Step[] steps = new Step[] { new Step() {
@Override
public void execute(RequestMonitor rm) {
if (fTracker != null) {
IGDBControl control = fTracker.getService(IGDBControl.class);
if (control != null) {
control.terminate(rm);
return;
}
}
rm.done();
return;
}
},
new Step() {
@Override
public void execute(RequestMonitor rm) {
fExecutor.execute(shutdownSeq);
}
} };
fExecutor.execute(new Sequence(fExecutor) {
@Override
public Step[] getSteps() {
return steps;
}
});
}
@Override
public <T> T getAdapter(Class<T> adapter) {
if (!adapter.equals(ITerminateHandler.class)) {
// Must force adapters to be loaded.
// Except in the case of terminate. Terminate can be used
// when running headless (no UI) and therefore we should not
// force the loading of UI plugins in this case.
// This can happen when running JUnit tests for example.
Platform.getAdapterManager().loadAdapter(this, adapter.getName());
}
return super.getAdapter(adapter);
}
@Override
public void launchRemoved(ILaunch launch) {
if (this.equals(launch)) {
// When the launch fails early, we may not have cleaned up
// properly. Let's do it here if needed.
if (DsfSession.isSessionActive(fSession.getId())) {
DsfSession.endSession(fSession);
}
fExecutor.shutdown();
fExecutor = null;
}
super.launchRemoved(launch);
}
/**
* Get the default GDB path if not specified in the launch or launch config.
*
* @since 5.0
*/
protected String getDefaultGDBPath() {
return Platform.getPreferencesService().getString(GdbPlugin.PLUGIN_ID,
IGdbDebugPreferenceConstants.PREF_DEFAULT_GDB_COMMAND,
IGDBLaunchConfigurationConstants.DEBUGGER_DEBUG_NAME_DEFAULT, null);
}
/**
* Returns the path to gdb.
*
* @since 5.0
*/
public IPath getGDBPath() {
try {
String gdb = getAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUG_NAME);
if (gdb == null) {
gdb = getLaunchConfiguration().getAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUG_NAME,
getDefaultGDBPath());
}
if (gdb != null) {
gdb = VariablesPlugin.getDefault().getStringVariableManager().performStringSubstitution(gdb, false);
return new Path(gdb);
} else {
return null;
}
} catch (CoreException e) {
GdbPlugin.log(e.getStatus());
return null;
}
}
/**
* Set the path to gdb
*
* @param path
* the path to gdb
* @since 5.0
*/
public void setGDBPath(String path) {
setAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUG_NAME, path);
}
/**
* This method actually launches 'gdb --version' to determine the version of
* the GDB that is being used. This method should ideally be called only
* once per session and the resulting version string stored for future uses.
*
* A timeout is scheduled which will kill the process if it takes too long.
*
* @since 5.0
*/
public String getGDBVersion() throws CoreException {
String cmd = getGDBPath().toOSString() + " --version"; //$NON-NLS-1$
// Parse cmd to properly handle spaces and such things (bug 458499)
String[] args = CommandLineUtil.argumentsToArray(cmd);
Process process = null;
Job timeoutJob = null;
try {
process = ProcessFactory.getFactory().exec(args, getLaunchEnvironment());
// Start a timeout job to make sure we don't get stuck waiting for
// an answer from a gdb that is hanging
// Bug 376203
final Process finalProc = process;
timeoutJob = new Job("GDB version timeout job") { //$NON-NLS-1$
{
setSystem(true);
}
@Override
protected IStatus run(IProgressMonitor arg) {
// Took too long. Kill the gdb process and
// let things clean up.
finalProc.destroy();
return Status.OK_STATUS;
}
};
timeoutJob.schedule(10000);
String streamOutput = readStream(process.getInputStream());
String gdbVersion = LaunchUtils.getGDBVersionFromText(streamOutput);
if (gdbVersion == null || gdbVersion.isEmpty()) {
Exception detailedException = null;
if (!streamOutput.isEmpty()) {
// We got some output but couldn't parse it. Make that
// output visible to the user in the error dialog.
detailedException = new Exception("Unexpected output format: \n\n" + streamOutput); //$NON-NLS-1$
} else {
// We got no output. Check if we got something on the error
// stream.
streamOutput = readStream(process.getErrorStream());
if (!streamOutput.isEmpty()) {
detailedException = new Exception(streamOutput);
}
}
throw new DebugException(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, DebugException.REQUEST_FAILED,
"Could not determine GDB version using command: " + StringUtil.join(args, " "), //$NON-NLS-1$ //$NON-NLS-2$
detailedException));
}
return gdbVersion;
} catch (IOException e) {
throw new DebugException(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, DebugException.REQUEST_FAILED,
"Error with command: " + StringUtil.join(args, " "), e));//$NON-NLS-1$ //$NON-NLS-2$
} finally {
// If we get here we are obviously not stuck reading the stream so
// we can cancel the timeout job.
// Note that it may already have executed, but that is not a
// problem.
if (timeoutJob != null) {
timeoutJob.cancel();
}
if (process != null) {
process.destroy();
}
}
}
/**
* Read from the specified stream and return what was read.
*
* @param stream
* The input stream to be used to read the data. This method will
* close the stream.
* @return The data read from the stream
* @throws IOException
* If an IOException happens when reading the stream
*/
private static String readStream(InputStream stream) throws IOException {
StringBuilder cmdOutput = new StringBuilder(200);
try {
Reader r = new InputStreamReader(stream);
BufferedReader reader = new BufferedReader(r);
String line;
while ((line = reader.readLine()) != null) {
cmdOutput.append(line);
cmdOutput.append('\n');
}
return cmdOutput.toString();
} finally {
// Cleanup to avoid leaking pipes
// Bug 345164
if (stream != null) {
try {
stream.close();
} catch (IOException e) {
}
}
}
}
/**
* Gets the CDT environment from the CDT project's configuration referenced
* by the launch
*
* @since 5.0
*/
public String[] getLaunchEnvironment() throws CoreException {
// Get the project
String projectName = getLaunchConfiguration().getAttribute(ICDTLaunchConfigurationConstants.ATTR_PROJECT_NAME,
(String) null);
IProject project = null;
if (projectName == null) {
IResource[] resources = getLaunchConfiguration().getMappedResources();
if (resources != null && resources.length > 0 && resources[0] instanceof IProject) {
project = (IProject) resources[0];
}
} else {
projectName = projectName.trim();
if (projectName.length() == 0) {
return null;
}
project = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName);
}
if (project == null || !project.isAccessible()) {
// No project
return null;
}
HashMap<String, String> envMap = new HashMap<String, String>();
ICProjectDescription projDesc = CoreModel.getDefault().getProjectDescription(project, false);
if (projDesc != null) {
String buildConfigID = getLaunchConfiguration()
.getAttribute(ICDTLaunchConfigurationConstants.ATTR_PROJECT_BUILD_CONFIG_ID, ""); //$NON-NLS-1$
ICConfigurationDescription cfg = null;
if (buildConfigID.length() != 0) {
cfg = projDesc.getConfigurationById(buildConfigID);
}
// if configuration is null fall-back to active
if (cfg == null) {
cfg = projDesc.getActiveConfiguration();
}
// Environment variables and inherited vars
IEnvironmentVariable[] vars = CCorePlugin.getDefault().getBuildEnvironmentManager().getVariables(cfg, true);
for (IEnvironmentVariable var : vars) {
envMap.put(var.getName(), var.getValue());
}
// Add variables from build info
ICdtVariableManager manager = CCorePlugin.getDefault().getCdtVariableManager();
ICdtVariable[] buildVars = manager.getVariables(cfg);
for (ICdtVariable var : buildVars) {
try {
// The project_classpath variable contributed by JDT is
// useless for running C/C++ binaries, but it can be lethal
// if it has a very large value that exceeds shell limit. See
// http://bugs.eclipse.org/bugs/show_bug.cgi?id=408522
if (!"project_classpath".equals(var.getName())) {//$NON-NLS-1$
String value = manager.resolveValue(var.getStringValue(), "", File.pathSeparator, cfg); //$NON-NLS-1$
envMap.put(var.getName(), value);
}
} catch (CdtVariableException e) {
// Some Eclipse dynamic variables can't be resolved
// dynamically... we don't care.
}
}
}
// Turn it into an envp format
List<String> strings = new ArrayList<String>(envMap.size());
for (Entry<String, String> entry : envMap.entrySet()) {
StringBuilder buffer = new StringBuilder(entry.getKey());
buffer.append('=').append(entry.getValue());
strings.add(buffer.toString());
}
return strings.toArray(new String[strings.size()]);
}
/**
* Get the location of the gdbinit file.
*
* @return gdbinit file location
* @throws CoreException
* @since 5.0
*/
public String getGDBInitFile() throws CoreException {
String defaultGdbInit = Platform.getPreferencesService().getString(GdbPlugin.PLUGIN_ID,
IGdbDebugPreferenceConstants.PREF_DEFAULT_GDB_INIT,
IGDBLaunchConfigurationConstants.DEBUGGER_GDB_INIT_DEFAULT, null);
return getLaunchConfiguration().getAttribute(IGDBLaunchConfigurationConstants.ATTR_GDB_INIT, defaultGdbInit);
}
/**
* Get the working directory.
*
* @return the working directory
* @throws CoreException
* @since 5.0
*/
public IPath getGDBWorkingDirectory() throws CoreException {
// First try to use the user-specified working directory for the
// debugged program.
// This is fine only with local debug.
// For remote debug, the working dir of the debugged program will be
// on remote device
// and hence not applicable. In such case we may just use debugged
// program path on host
// as the working dir for GDB.
// However, we cannot find a standard/common way to distinguish
// remote debug from local
// debug. For instance, a local debug may also use gdbserver+gdb. So
// it's up to each
// debugger implementation to make the distinction.
//
IPath path = null;
String location = getLaunchConfiguration().getAttribute(ICDTLaunchConfigurationConstants.ATTR_WORKING_DIRECTORY,
(String) null);
if (location != null) {
String expandedLocation = VariablesPlugin.getDefault().getStringVariableManager()
.performStringSubstitution(location);
if (!expandedLocation.isEmpty()) {
path = new Path(expandedLocation);
}
}
if (path != null) {
// Some validity check. Should have been done by UI code.
if (path.isAbsolute()) {
File dir = new File(path.toPortableString());
if (!dir.isDirectory())
path = null;
} else {
IResource res = ResourcesPlugin.getWorkspace().getRoot().findMember(path);
if (res instanceof IContainer && res.exists()) {
path = res.getLocation();
} else
// Relative but not found in workspace.
path = null;
}
}
if (path == null) {
// default working dir is the project if this config has a
// project
ICProject cp = LaunchUtils.getCProject(getLaunchConfiguration());
if (cp != null) {
IProject p = cp.getProject();
path = p.getLocation();
} else {
// no meaningful value found. Just return null.
}
}
return path;
}
/**
* Get the program arguments
*
* @return program arguments
* @throws CoreException
* @since 5.0
*/
public String getProgramArguments() throws CoreException {
String programArguments = getLaunchConfiguration()
.getAttribute(ICDTLaunchConfigurationConstants.ATTR_PROGRAM_ARGUMENTS, (String) null);
if (programArguments != null) {
programArguments = VariablesPlugin.getDefault().getStringVariableManager()
.performStringSubstitution(programArguments);
}
return programArguments;
}
/**
* Return the program path
*
* @return the program path
* @since 5.0
*/
public String getProgramPath() throws CoreException {
String programPath = getAttribute(ICDTLaunchConfigurationConstants.ATTR_PROGRAM_NAME);
if (programPath == null) {
programPath = getLaunchConfiguration().getAttribute(ICDTLaunchConfigurationConstants.ATTR_PROGRAM_NAME,
(String) null);
}
return programPath;
}
/**
* Sets the program path
*
* @param programPath
* the program path
* @throws CoreException
* @since 5.0
*/
public void setProgramPath(String programPath) throws CoreException {
setAttribute(ICDTLaunchConfigurationConstants.ATTR_PROGRAM_NAME, programPath);
}
/**
* Return shared library paths
*
* @return shared library paths
* @throws CoreException
* @since 5.0
*/
public List<String> getSharedLibraryPaths() throws CoreException {
return getLaunchConfiguration().getAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_SOLIB_PATH,
new ArrayList<String>(0));
}
/**
* return the environment
*
* @return the environment
* @throws CoreException
* @since 5.0
*/
public Properties getEnvironmentVariables() throws CoreException {
Properties envVariables = new Properties();
// if the attribute ATTR_APPEND_ENVIRONMENT_VARIABLES is set,
// the LaunchManager will return both the new variables and the
// existing ones.
// That would force us to delete all the variables in GDB, and then
// re-create then all
// that is not very efficient. So, let's fool the LaunchManager into
// returning just the
// list of new variables.
boolean append = getLaunchConfiguration().getAttribute(ILaunchManager.ATTR_APPEND_ENVIRONMENT_VARIABLES, true);
String[] properties;
if (append) {
ILaunchConfigurationWorkingCopy wc = getLaunchConfiguration().copy(""); //$NON-NLS-1$
// Don't save this change, it is just temporary, and in just a
// copy of our launchConfig.
wc.setAttribute(ILaunchManager.ATTR_APPEND_ENVIRONMENT_VARIABLES, false);
properties = DebugPlugin.getDefault().getLaunchManager().getEnvironment(wc);
} else {
// We're getting rid of the environment anyway, so this call
// will only yield the new variables.
properties = DebugPlugin.getDefault().getLaunchManager().getEnvironment(getLaunchConfiguration());
}
if (properties == null) {
properties = new String[0];
}
for (String property : properties) {
int idx = property.indexOf('=');
if (idx != -1) {
String key = property.substring(0, idx);
String value = property.substring(idx + 1);
envVariables.setProperty(key, value);
} else {
envVariables.setProperty(property, ""); //$NON-NLS-1$
}
}
return envVariables;
}
/**
* Get whether to clear the environment before applying the variables
*
* @return clear
* @throws CoreException
* @since 5.0
*/
public boolean getClearEnvironment() throws CoreException {
return !getLaunchConfiguration().getAttribute(ILaunchManager.ATTR_APPEND_ENVIRONMENT_VARIABLES, true);
}
/**
* Get whether to update thread list on suspend
*
* @return whether
* @throws CoreException
* @since 5.0
*/
public boolean getUpdateThreadListOnSuspend() throws CoreException {
return getLaunchConfiguration().getAttribute(
IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_UPDATE_THREADLIST_ON_SUSPEND,
IGDBLaunchConfigurationConstants.DEBUGGER_UPDATE_THREADLIST_ON_SUSPEND_DEFAULT);
}
/**
* Set the launch target
*
* @param launchTarget
* the launch target
* @since 5.0
*/
public void setLaunchTarget(ILaunchTarget launchTarget) {
this.fLaunchTarget = launchTarget;
}
/**
* Return the launch target
*
* @since 5.0
*/
@Override
public ILaunchTarget getLaunchTarget() {
return fLaunchTarget;
}
}