/*******************************************************************************
* Copyright (c) 2004, 2010 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
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* QNX Software Systems - Initial API and implementation
* Anton Leherbauer (Wind River Systems) - bugs 205108, 212632, 224187
* Ken Ryall (Nokia) - bug 188116
*******************************************************************************/
package org.eclipse.cdt.launch.internal;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.IProcessInfo;
import org.eclipse.cdt.core.IProcessList;
import org.eclipse.cdt.core.IBinaryParser.IBinaryObject;
import org.eclipse.cdt.core.model.ICProject;
import org.eclipse.cdt.debug.core.CDIDebugModel;
import org.eclipse.cdt.debug.core.CDebugUtils;
import org.eclipse.cdt.debug.core.ICDIDebugger;
import org.eclipse.cdt.debug.core.ICDIDebugger2;
import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants;
import org.eclipse.cdt.debug.core.ICDebugConfiguration;
import org.eclipse.cdt.debug.core.cdi.CDIException;
import org.eclipse.cdt.debug.core.cdi.ICDISession;
import org.eclipse.cdt.debug.core.cdi.model.ICDIRuntimeOptions;
import org.eclipse.cdt.debug.core.cdi.model.ICDITarget;
import org.eclipse.cdt.launch.AbstractCLaunchDelegate;
import org.eclipse.cdt.launch.internal.ui.LaunchMessages;
import org.eclipse.cdt.launch.internal.ui.LaunchUIPlugin;
import org.eclipse.cdt.utils.pty.PTY;
import org.eclipse.cdt.utils.spawner.ProcessFactory;
import org.eclipse.core.resources.IProject;
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.NullProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubProgressMonitor;
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.model.IProcess;
/**
* The launch configuration delegate for the CDI debugger session types.
*/
public class LocalCDILaunchDelegate extends AbstractCLaunchDelegate {
/* (non-Javadoc)
* @see org.eclipse.cdt.launch.AbstractCLaunchDelegate#launch(org.eclipse.debug.core.ILaunchConfiguration, java.lang.String, org.eclipse.debug.core.ILaunch, org.eclipse.core.runtime.IProgressMonitor)
*/
public void launch(ILaunchConfiguration config, String mode, ILaunch launch, IProgressMonitor monitor) throws CoreException {
if (monitor == null) {
monitor = new NullProgressMonitor();
}
if (mode.equals(ILaunchManager.RUN_MODE)) {
runLocalApplication(config, launch, monitor);
}
if (mode.equals(ILaunchManager.DEBUG_MODE)) {
launchDebugger(config, launch, monitor);
}
}
private void runLocalApplication(ILaunchConfiguration config, ILaunch launch, IProgressMonitor monitor) throws CoreException {
monitor.beginTask(LaunchMessages.LocalCDILaunchDelegate_0, 10);
if (monitor.isCanceled()) {
return;
}
monitor.worked(1);
try {
IPath exePath = CDebugUtils.verifyProgramPath(config);
File wd = getWorkingDirectory(config);
if (wd == null) {
wd = new File(System.getProperty("user.home", ".")); //$NON-NLS-1$ //$NON-NLS-2$
}
String arguments[] = getProgramArgumentsArray(config);
ArrayList command = new ArrayList(1 + arguments.length);
command.add(exePath.toOSString());
command.addAll(Arrays.asList(arguments));
String[] commandArray = (String[])command.toArray(new String[command.size()]);
boolean usePty = config.getAttribute(ICDTLaunchConfigurationConstants.ATTR_USE_TERMINAL, ICDTLaunchConfigurationConstants.USE_TERMINAL_DEFAULT);
monitor.worked(2);
Process process = exec(commandArray, getEnvironment(config), wd, usePty);
monitor.worked(6);
DebugPlugin.newProcess(launch, process, renderProcessLabel(commandArray[0]));
} finally {
monitor.done();
}
}
private void launchDebugger(ILaunchConfiguration config, ILaunch launch, IProgressMonitor monitor) throws CoreException {
monitor.beginTask(LaunchMessages.LocalCDILaunchDelegate_1, 10);
if (monitor.isCanceled()) {
return;
}
try {
String debugMode = config.getAttribute(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_START_MODE, ICDTLaunchConfigurationConstants.DEBUGGER_MODE_RUN);
if (debugMode.equals(ICDTLaunchConfigurationConstants.DEBUGGER_MODE_RUN)) {
launchLocalDebugSession(config, launch, monitor);
}
if (debugMode.equals(ICDTLaunchConfigurationConstants.DEBUGGER_MODE_ATTACH)) {
launchAttachDebugSession(config, launch, monitor);
}
if (debugMode.equals(ICDTLaunchConfigurationConstants.DEBUGGER_MODE_CORE)) {
launchCoreDebugSession(config, launch, monitor);
}
} finally {
monitor.done();
}
}
private void launchLocalDebugSession(ILaunchConfiguration config, ILaunch launch, IProgressMonitor monitor) throws CoreException {
if (monitor.isCanceled()) {
return;
}
monitor.subTask(LaunchMessages.LocalCDILaunchDelegate_2);
ICDISession dsession = null;
try {
IPath exePath = CDebugUtils.verifyProgramPath(config);
ICProject project = CDebugUtils.verifyCProject(config);
IBinaryObject exeFile = null;
if (exePath != null) {
exeFile = verifyBinary(project, exePath);
}
ICDebugConfiguration debugConfig = getDebugConfig(config);
setDefaultSourceLocator(launch, config);
dsession = createCDISession(config, launch, debugConfig, monitor);
monitor.worked(6);
setRuntimeOptions(config, dsession);
monitor.worked(1);
boolean stopInMain = config.getAttribute(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_STOP_AT_MAIN, false);
String stopSymbol = null;
if (stopInMain)
stopSymbol = launch.getLaunchConfiguration().getAttribute(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_STOP_AT_MAIN_SYMBOL, ICDTLaunchConfigurationConstants.DEBUGGER_STOP_AT_MAIN_SYMBOL_DEFAULT);
ICDITarget[] targets = dsession.getTargets();
for(int i = 0; i < targets.length; i++) {
Process process = targets[i].getProcess();
IProcess iprocess = null;
if (process != null) {
iprocess = DebugPlugin.newProcess(launch, process, renderProcessLabel(exePath.toOSString()), getDefaultProcessMap());
}
CDIDebugModel.newDebugTarget(launch, project.getProject(), targets[i], renderTargetLabel(debugConfig), iprocess, exeFile, true, false, stopSymbol, true);
}
} catch(CoreException e) {
try {
if (dsession != null)
dsession.terminate();
} catch(CDIException e1) {
// ignore
}
throw e;
} finally {
monitor.done();
}
}
private void launchAttachDebugSession(ILaunchConfiguration config, ILaunch launch, IProgressMonitor monitor) throws CoreException {
if (monitor.isCanceled()) {
return;
}
monitor.subTask(LaunchMessages.LocalCDILaunchDelegate_3);
ILaunchConfigurationWorkingCopy wc = null;
int pid = config.getAttribute(ICDTLaunchConfigurationConstants.ATTR_ATTACH_PROCESS_ID, -1);
if (pid == -1) {
pid = promptForProcessID(config);
if (pid == -1) {
cancel(LaunchMessages.LocalCDILaunchDelegate_4, ICDTLaunchConfigurationConstants.ERR_NO_PROCESSID);
}
wc = config.getWorkingCopy();
wc.setAttribute(ICDTLaunchConfigurationConstants.ATTR_ATTACH_PROCESS_ID, pid);
try {
wc.doSave().launch(ILaunchManager.DEBUG_MODE, new SubProgressMonitor(monitor, 9));
} finally {
// We need to reset the process id because the working copy will be saved
// when the target is terminated
wc.setAttribute(ICDTLaunchConfigurationConstants.ATTR_ATTACH_PROCESS_ID, (String)null);
wc.doSave();
}
cancel("", -1); //$NON-NLS-1$
}
IPath exePath = CDebugUtils.verifyProgramPath(config);
if (exePath == null) {
exePath= getProgramPathForPid(pid);
}
ICProject project = CDebugUtils.verifyCProject(config);
IBinaryObject exeFile = null;
if (exePath != null) {
exeFile = verifyBinary(project, exePath);
}
ICDebugConfiguration debugConfig = getDebugConfig(config);
setDefaultSourceLocator(launch, config);
ICDISession dsession = createCDISession(config, launch,debugConfig, monitor);
monitor.worked(7);
try {
ICDITarget[] targets = dsession.getTargets();
for(int i = 0; i < targets.length; i++) {
CDIDebugModel.newDebugTarget(launch, project.getProject(), targets[i], renderTargetLabel(debugConfig), null, exeFile, true, true, false);
}
} catch(CoreException e) {
try {
dsession.terminate();
} catch(CDIException e1) {
// ignore
}
throw e;
} finally {
if (wc != null)
wc.setAttribute(ICDTLaunchConfigurationConstants.ATTR_ATTACH_PROCESS_ID, (String)null);
monitor.done();
}
}
private IPath getProgramPathForPid(int pid) {
IProcessList processList= null;
try {
processList= CCorePlugin.getDefault().getProcessList();
} catch (CoreException exc) {
// ignored on purpose
}
if (processList != null) {
IProcessInfo[] pInfos= processList.getProcessList();
for (int i = 0; i < pInfos.length; i++) {
IProcessInfo processInfo = pInfos[i];
if (processInfo.getPid() == pid) {
final String name= processInfo.getName();
if (name != null) {
return new Path(name);
}
break;
}
}
}
return null;
}
private void launchCoreDebugSession(ILaunchConfiguration config, ILaunch launch, IProgressMonitor monitor) throws CoreException {
if (monitor.isCanceled()) {
return;
}
monitor.beginTask(LaunchMessages.LocalCDILaunchDelegate_5, 10);
ICDISession dsession = null;
ILaunchConfigurationWorkingCopy wc = null;
ICDebugConfiguration debugConfig = getDebugConfig(config);
String path = config.getAttribute(ICDTLaunchConfigurationConstants.ATTR_COREFILE_PATH, (String)null);
if (path == null || path.length() == 0) {
ICProject project = CDebugUtils.verifyCProject(config);
IPath corefile = promptForCoreFilePath((IProject)project.getResource(), debugConfig);
if (corefile == null) {
cancel(LaunchMessages.LocalCDILaunchDelegate_6, ICDTLaunchConfigurationConstants.ERR_NO_COREFILE);
}
File file = new File(corefile.toString());
if (!file.exists() || !file.canRead()) {
cancel(LaunchMessages.LocalCDILaunchDelegate_7, ICDTLaunchConfigurationConstants.ERR_NO_COREFILE);
}
wc = config.getWorkingCopy();
wc.setAttribute(ICDTLaunchConfigurationConstants.ATTR_COREFILE_PATH, corefile.toString());
wc.launch(ILaunchManager.DEBUG_MODE, new SubProgressMonitor(monitor, 9));
wc.setAttribute(ICDTLaunchConfigurationConstants.ATTR_COREFILE_PATH, (String)null);
cancel("", -1); //$NON-NLS-1$
}
IPath exePath = CDebugUtils.verifyProgramPath(config);
ICProject project = CDebugUtils.verifyCProject(config);
IBinaryObject exeFile = null;
if (exePath != null) {
exeFile = verifyBinary(project, exePath);
}
setDefaultSourceLocator(launch, config);
dsession = createCDISession(config, launch, debugConfig, monitor);
monitor.worked(7);
try {
ICDITarget[] targets = dsession.getTargets();
for(int i = 0; i < targets.length; i++) {
Process process = targets[i].getProcess();
IProcess iprocess = null;
if (process != null) {
iprocess = DebugPlugin.newProcess(launch, process, renderProcessLabel(exePath.toOSString()), getDefaultProcessMap());
}
CDIDebugModel.newDebugTarget(launch, project.getProject(), targets[i], renderTargetLabel(debugConfig), iprocess, exeFile, true, false, false);
}
} catch(CoreException e) {
try {
if (dsession != null)
dsession.terminate();
} catch(CDIException e1) {
// ignore
}
throw e;
} finally {
monitor.done();
}
}
private ICDISession launchOldDebugSession(ILaunchConfiguration config, ILaunch launch, ICDIDebugger debugger, IProgressMonitor monitor) throws CoreException {
IBinaryObject exeFile = null;
IPath exePath = CDebugUtils.verifyProgramPath(config);
ICProject project = CDebugUtils.verifyCProject(config);
if (exePath != null) {
exeFile = verifyBinary(project, exePath);
}
return debugger.createDebuggerSession(launch, exeFile, monitor);
}
private ICDISession launchDebugSession(ILaunchConfiguration config, ILaunch launch, ICDIDebugger2 debugger, IProgressMonitor monitor) throws CoreException {
IPath path = CDebugUtils.verifyProgramPath(config);
File exeFile = path != null ? path.toFile() : null;
return debugger.createSession(launch, exeFile, monitor);
}
/* (non-Javadoc)
* @see org.eclipse.cdt.launch.AbstractCLaunchDelegate#getPluginID()
*/
protected String getPluginID() {
return LaunchUIPlugin.getUniqueIdentifier();
}
/**
* Performs a runtime exec on the given command line in the context of the
* specified working directory, and returns the resulting process. If the
* current runtime does not support the specification of a working
* directory, the status handler for error code
* <code>ERR_WORKING_DIRECTORY_NOT_SUPPORTED</code> is queried to see if
* the exec should be re-executed without specifying a working directory.
*
* @param cmdLine
* the command line
* @param workingDirectory
* the working directory, or <code>null</code>
* @return the resulting process or <code>null</code> if the exec is
* cancelled
* @see Runtime
*/
protected Process exec(String[] cmdLine, String[] environ, File workingDirectory, boolean usePty) throws CoreException {
Process p = null;
try {
if (workingDirectory == null) {
p = ProcessFactory.getFactory().exec(cmdLine, environ);
}
else {
if (usePty && PTY.isSupported()) {
p = ProcessFactory.getFactory().exec(cmdLine, environ, workingDirectory, new PTY());
}
else {
p = ProcessFactory.getFactory().exec(cmdLine, environ, workingDirectory);
}
}
}
catch(IOException e) {
if (p != null) {
p.destroy();
}
abort(LaunchMessages.LocalCDILaunchDelegate_8, e, ICDTLaunchConfigurationConstants.ERR_INTERNAL_ERROR);
}
catch(NoSuchMethodError e) {
// attempting launches on 1.2.* - no ability to set working
// directory
IStatus status = new Status(IStatus.ERROR, LaunchUIPlugin.getUniqueIdentifier(), ICDTLaunchConfigurationConstants.ERR_WORKING_DIRECTORY_NOT_SUPPORTED, LaunchMessages.LocalCDILaunchDelegate_9, e);
IStatusHandler handler = DebugPlugin.getDefault().getStatusHandler(status);
if (handler != null) {
Object result = handler.handleStatus(status, this);
if (result instanceof Boolean && ((Boolean)result).booleanValue()) {
p = exec(cmdLine, environ, null, usePty);
}
}
}
return p;
}
protected int promptForProcessID(ILaunchConfiguration config) throws CoreException {
IStatus fPromptStatus = new Status(IStatus.INFO, "org.eclipse.debug.ui", 200, "", null); //$NON-NLS-1$//$NON-NLS-2$
IStatus processPrompt = new Status(IStatus.INFO, "org.eclipse.cdt.launch", 100, "", null); //$NON-NLS-1$//$NON-NLS-2$
// consult a status handler
IStatusHandler prompter = DebugPlugin.getDefault().getStatusHandler(fPromptStatus);
if (prompter != null) {
Object result = prompter.handleStatus(processPrompt, config);
if (result instanceof Integer) {
return ((Integer) result).intValue();
}
}
return -1;
}
protected IPath promptForCoreFilePath(final IProject project, final ICDebugConfiguration debugConfig) throws CoreException {
IStatus fPromptStatus = new Status(IStatus.INFO, "org.eclipse.debug.ui", 200, "", null); //$NON-NLS-1$//$NON-NLS-2$
IStatus processPrompt = new Status(IStatus.INFO, "org.eclipse.cdt.launch", 1001, "", null); //$NON-NLS-1$//$NON-NLS-2$
// consult a status handler
IStatusHandler prompter = DebugPlugin.getDefault().getStatusHandler(fPromptStatus);
if (prompter != null) {
Object result = prompter.handleStatus(processPrompt, new Object[]{ project, debugConfig });
if (result instanceof IPath) {
return (IPath) result;
}
}
return null;
}
/* (non-Javadoc)
* @see org.eclipse.cdt.launch.AbstractCLaunchDelegate#preLaunchCheck(org.eclipse.debug.core.ILaunchConfiguration, java.lang.String, org.eclipse.core.runtime.IProgressMonitor)
*/
public boolean preLaunchCheck(ILaunchConfiguration config, String mode, IProgressMonitor monitor) throws CoreException {
// no pre launch check for core file
if (mode.equals(ILaunchManager.DEBUG_MODE)) {
if (ICDTLaunchConfigurationConstants.DEBUGGER_MODE_CORE.equals(config.getAttribute(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_START_MODE, ICDTLaunchConfigurationConstants.DEBUGGER_MODE_RUN)))
return true;
}
return super.preLaunchCheck(config, mode, monitor);
}
private void setRuntimeOptions(ILaunchConfiguration config, ICDISession session) throws CoreException {
String arguments[] = getProgramArgumentsArray(config);
try {
ICDITarget[] dtargets = session.getTargets();
for(int i = 0; i < dtargets.length; ++i) {
ICDIRuntimeOptions opt = dtargets[i].getRuntimeOptions();
opt.setArguments(arguments);
File wd = getWorkingDirectory(config);
if (wd != null) {
opt.setWorkingDirectory(wd.getAbsolutePath());
}
opt.setEnvironment(getEnvironmentAsProperty(config));
}
} catch (CDIException e) {
abort(LaunchMessages.LocalCDILaunchDelegate_10, e, ICDTLaunchConfigurationConstants.ERR_INTERNAL_ERROR);
}
}
private ICDISession createCDISession(ILaunchConfiguration config, ILaunch launch, ICDebugConfiguration debugConfig, IProgressMonitor monitor) throws CoreException {
ICDISession session = null;
ICDIDebugger debugger = debugConfig.createDebugger();
if (debugger instanceof ICDIDebugger2)
session = launchDebugSession(config, launch, (ICDIDebugger2)debugger, monitor);
else
// support old debugger types
session = launchOldDebugSession(config, launch, debugger, monitor);
return session;
}
@Override
public boolean buildForLaunch(ILaunchConfiguration configuration, String mode, IProgressMonitor monitor) throws CoreException {
// Never build for attach. Bug 188116
String debugMode = configuration.getAttribute(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_START_MODE, ICDTLaunchConfigurationConstants.DEBUGGER_MODE_RUN);
if (debugMode.equals(ICDTLaunchConfigurationConstants.DEBUGGER_MODE_ATTACH))
return false;
return super.buildForLaunch(configuration, mode, monitor);
}
}