/*******************************************************************************
* Copyright (c) 2015, 2016 Wind River Systems, Inc. and others. All rights reserved.
* This program and the accompanying materials are made available under the terms
* of the Eclipse Public License v1.0 which accompanies this distribution, and is
* available at http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Wind River Systems - initial API and implementation
*******************************************************************************/
package org.eclipse.tcf.te.tcf.launch.cdt.launching;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants;
import org.eclipse.cdt.debug.internal.core.DebugStringVariableSubstitutor;
import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
import org.eclipse.cdt.dsf.concurrent.DsfExecutor;
import org.eclipse.cdt.dsf.concurrent.DsfRunnable;
import org.eclipse.cdt.dsf.concurrent.ImmediateRequestMonitor;
import org.eclipse.cdt.dsf.concurrent.Query;
import org.eclipse.cdt.dsf.datamodel.IDMContext;
import org.eclipse.cdt.dsf.debug.service.command.ICommandListener;
import org.eclipse.cdt.dsf.debug.service.command.ICommandResult;
import org.eclipse.cdt.dsf.debug.service.command.ICommandToken;
import org.eclipse.cdt.dsf.gdb.IGDBLaunchConfigurationConstants;
import org.eclipse.cdt.dsf.gdb.launching.GdbLaunch;
import org.eclipse.cdt.dsf.gdb.launching.GdbLaunchDelegate;
import org.eclipse.cdt.dsf.gdb.service.IGDBBackend;
import org.eclipse.cdt.dsf.gdb.service.IGDBProcesses;
import org.eclipse.cdt.dsf.gdb.service.command.IGDBControl;
import org.eclipse.cdt.dsf.mi.service.IMIBackend;
import org.eclipse.cdt.dsf.mi.service.command.commands.MIGDBExit;
import org.eclipse.cdt.dsf.service.DsfServicesTracker;
import org.eclipse.cdt.dsf.service.DsfSession;
import org.eclipse.core.runtime.Assert;
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.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.core.variables.VariablesPlugin;
import org.eclipse.debug.core.DebugException;
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.model.ISourceLocator;
import org.eclipse.osgi.util.NLS;
import org.eclipse.tcf.protocol.IPeer;
import org.eclipse.tcf.te.core.utils.text.StringUtil;
import org.eclipse.tcf.te.runtime.callback.Callback;
import org.eclipse.tcf.te.runtime.services.ServiceUtils;
import org.eclipse.tcf.te.runtime.utils.Host;
import org.eclipse.tcf.te.tcf.core.streams.StreamsDataReceiver;
import org.eclipse.tcf.te.tcf.launch.cdt.activator.Activator;
import org.eclipse.tcf.te.tcf.launch.cdt.interfaces.IGdbserverLaunchHandlerDelegate;
import org.eclipse.tcf.te.tcf.launch.cdt.interfaces.IRemoteTEConfigurationConstants;
import org.eclipse.tcf.te.tcf.launch.cdt.nls.Messages;
import org.eclipse.tcf.te.tcf.launch.cdt.preferences.IPreferenceKeys;
import org.eclipse.tcf.te.tcf.launch.cdt.utils.TEHelper;
import org.eclipse.tcf.te.tcf.locator.interfaces.nodes.IPeerNode;
import org.eclipse.tcf.te.tcf.processes.core.launcher.ProcessLauncher;
/**
* Abstract launch delegate implementation handling launching the gdbserver via TCF/TE.
*/
@SuppressWarnings("restriction")
public abstract class TEGdbAbstractLaunchDelegate extends GdbLaunchDelegate {
private String fCurrentGdbServerPort;
/**
* Constructor
*/
public TEGdbAbstractLaunchDelegate() {
super();
}
/**
* Constructor
*
* @param requireCProject <code>True</code> if a C project is required for launching,
* <code>false</code> otherwise.
*/
public TEGdbAbstractLaunchDelegate(boolean requireCProject) {
super(requireCProject);
}
/* (non-Javadoc)
* @see org.eclipse.cdt.dsf.gdb.launching.GdbLaunchDelegate#createGdbLaunch(org.eclipse.debug.core.ILaunchConfiguration, java.lang.String, org.eclipse.debug.core.model.ISourceLocator)
*/
@Override
protected GdbLaunch createGdbLaunch(ILaunchConfiguration configuration, String mode, ISourceLocator locator) throws CoreException {
return new TEGdbLaunch(configuration, mode, locator);
}
/* (non-Javadoc)
* @see org.eclipse.cdt.dsf.gdb.launching.GdbLaunchDelegate#launch(org.eclipse.debug.core.ILaunchConfiguration, java.lang.String, org.eclipse.debug.core.ILaunch, org.eclipse.core.runtime.IProgressMonitor)
*/
@Override
public void launch(ILaunchConfiguration config, String mode, ILaunch launch, IProgressMonitor monitor) throws CoreException {
// If not of the expected type --> return immediately
if (!(launch instanceof GdbLaunch)) return;
// Initialize TE
Activator.getDefault().initializeTE();
// Get the peer from the launch configuration
IPeer peer = TEHelper.getCurrentConnection(config).getPeer();
// Determine if the launch is an attach launch
final boolean isAttachLaunch = ICDTLaunchConfigurationConstants.ID_LAUNCH_C_ATTACH.equals(config.getType().getIdentifier());
// Get the executable path (run/debug application) or the PID (attach to application)
IPath exePath = checkBinaryDetails(config);
String remoteExePath = config.getAttribute(IRemoteTEConfigurationConstants.ATTR_REMOTE_PATH, (String)null);
String remotePID = config.getAttribute(IRemoteTEConfigurationConstants.ATTR_REMOTE_PID, (String)null);
// Not an attach launch and the executable is not specified --> abort
if (!isAttachLaunch && exePath == null) {
abort(Messages.TEGdbAbstractLaunchDelegate_no_program, null, ICDTLaunchConfigurationConstants.ERR_PROGRAM_NOT_EXIST);
}
// Not an attach launch and the remote executable is not specified --> abort
if (!isAttachLaunch && remoteExePath == null) {
abort(Messages.TEGdbAbstractLaunchDelegate_no_remote_path, null, ICDTLaunchConfigurationConstants.ERR_PROGRAM_NOT_EXIST);
}
// Attach launch and the remote PID is not specified --> abort
if (isAttachLaunch && remotePID == null) {
abort(Messages.TEGdbAbstractLaunchDelegate_no_pid, null, ICDTLaunchConfigurationConstants.ERR_NO_PROCESSID);
}
// If an executable path is specified, download the binary if needed
if (!isAttachLaunch && exePath != null && remoteExePath != null) {
monitor.setTaskName(Messages.TEGdbAbstractLaunchDelegate_downloading);
boolean skipDownload = config.getAttribute(IRemoteTEConfigurationConstants.ATTR_SKIP_DOWNLOAD_TO_TARGET, false);
if (!skipDownload) {
try {
TEHelper.remoteFileTransfer(peer, exePath.toString(), remoteExePath, new SubProgressMonitor(monitor, 80));
}
catch (IOException e) {
abort(NLS.bind(Messages.TEGdbAbstractLaunchDelegate_filetransferFailed, e.getLocalizedMessage()), e, ICDTLaunchConfigurationConstants.ERR_PROGRAM_NOT_EXIST);
}
}
}
// Launch gdbserver on target
final AtomicReference<String> gdbserverPortNumber = new AtomicReference<String>(config.getAttribute(IRemoteTEConfigurationConstants.ATTR_GDBSERVER_PORT, TEHelper.getStringPreferenceValue(isAttachLaunch ? IPreferenceKeys.PREF_GDBSERVER_PORT_ATTACH : IPreferenceKeys.PREF_GDBSERVER_PORT)));
final AtomicReference<String> gdbserverPortNumberMappedTo = new AtomicReference<String>(config.getAttribute(IRemoteTEConfigurationConstants.ATTR_GDBSERVER_PORT_MAPPED_TO, TEHelper.getStringPreferenceValue(isAttachLaunch ? IPreferenceKeys.PREF_GDBSERVER_PORT_ATTACH_MAPPED_TO : IPreferenceKeys.PREF_GDBSERVER_PORT_MAPPED_TO)));
final String gdbserverCommand = config.getAttribute(IRemoteTEConfigurationConstants.ATTR_GDBSERVER_COMMAND, TEHelper.getStringPreferenceValue(isAttachLaunch ? IPreferenceKeys.PREF_GDBSERVER_COMMAND_ATTACH : IPreferenceKeys.PREF_GDBSERVER_COMMAND));
final List<String> gdbserverPortNumberAlternatives = config.getAttribute(IRemoteTEConfigurationConstants.ATTR_GDBSERVER_PORT_ALTERNATIVES, TEHelper.getListPreferenceValue(isAttachLaunch ? IPreferenceKeys.PREF_GDBSERVER_PORT_ATTACH_ALTERNATIVES : IPreferenceKeys.PREF_GDBSERVER_PORT_ALTERNATIVES));
final List<String> gdbserverPortNumberMappedToAlternatives = config.getAttribute(IRemoteTEConfigurationConstants.ATTR_GDBSERVER_PORT_MAPPED_TO_ALTERNATIVES, TEHelper.getListPreferenceValue(isAttachLaunch ? IPreferenceKeys.PREF_GDBSERVER_PORT_ATTACH_MAPPED_TO_ALTERNATIVES : IPreferenceKeys.PREF_GDBSERVER_PORT_MAPPED_TO_ALTERNATIVES));
final String gdbinitFile = config.getAttribute(IRemoteTEConfigurationConstants.ATTR_GDB_INIT, (String)null);
// Remember the originally configured port number and mapped to port number
final String origGdbserverPortNumber = gdbserverPortNumber.get();
final String origGdbserverPortNumberMappedTo = gdbserverPortNumberMappedTo.get();
final AtomicReference<String> origGdbserverOutput = new AtomicReference<String>(null);
ProcessLauncher launcher = null;
final AtomicBoolean gdbserverLaunchRetry = new AtomicBoolean(false);
final AtomicInteger indexAlternatives = new AtomicInteger(0);
do {
fCurrentGdbServerPort = gdbserverPortNumber.get();
gdbserverLaunchRetry.set(false);
final AtomicBoolean gdbServerStarted = new AtomicBoolean(false);
final AtomicBoolean gdbServerReady = new AtomicBoolean(false);
final AtomicBoolean gdbServerExited = new AtomicBoolean(false);
final StringBuilder gdbServerOutput = new StringBuilder();
final Object lock = new Object();
String commandArguments = ""; //$NON-NLS-1$
Map<String,String> commandEnv = null;
if (isAttachLaunch) {
commandArguments = "--once --multi :" + gdbserverPortNumber.get(); //$NON-NLS-1$
monitor.setTaskName(Messages.TEGdbAbstractLaunchDelegate_attaching_program);
} else {
commandArguments = "--once :" + gdbserverPortNumber.get() + " " + TEHelper.spaceEscapify(remoteExePath); //$NON-NLS-1$ //$NON-NLS-2$
String arguments = getProgramArguments(config);
commandEnv = config.getAttribute(ILaunchManager.ATTR_ENVIRONMENT_VARIABLES, (Map<String,String>)null);
if (arguments != null && !arguments.equals("")) { //$NON-NLS-1$
commandArguments += " " + arguments.replaceAll("\\r", " ").replaceAll("\\n", " "); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
}
monitor.setTaskName(Messages.TEGdbAbstractLaunchDelegate_starting_program);
}
final GdbLaunch l = (GdbLaunch) launch;
final Callback callback = new Callback() {
@Override
protected void internalDone(Object caller, IStatus status) {
if (!status.isOK()) {
gdbServerOutput.append(status.getMessage());
gdbServerExited.set(true);
synchronized (lock) {
lock.notifyAll();
}
}
else {
gdbServerStarted.set(true);
}
super.internalDone(caller, status);
}
};
StreamsDataReceiver.Listener listener = new StreamsDataReceiver.Listener() {
@Override
public void dataReceived(String data) {
gdbServerOutput.append(data);
if (data.contains("Listening on port")) { //$NON-NLS-1$
gdbServerReady.set(true);
synchronized (lock) {
lock.notifyAll();
}
}
else if (data.contains("GDBserver exiting") || data.contains("Exiting")) { //$NON-NLS-1$ //$NON-NLS-2$
// Check if the gdbserver exited because the port is already in use
if (gdbServerOutput.toString().contains("Address already in use.")) { //$NON-NLS-1$
// If we have still alternatives, then retry the gdbserver launch
// with an alternative port
if (gdbserverPortNumberAlternatives != null && !gdbserverPortNumberAlternatives.isEmpty()) {
String newPort = null;
String newPortMappedTo = null;
do {
newPort = gdbserverPortNumberAlternatives.get(indexAlternatives.get());
if (gdbserverPortNumber.get().equals(newPort)) {
newPort = null;
} else {
newPortMappedTo = gdbserverPortNumberMappedToAlternatives != null && !gdbserverPortNumberMappedToAlternatives.isEmpty() ? gdbserverPortNumberMappedToAlternatives.get(indexAlternatives.get()) : null;
}
indexAlternatives.getAndIncrement();
} while (newPort == null && indexAlternatives.get() < gdbserverPortNumberAlternatives.size());
if (newPort != null) {
// Remember the original error
if (origGdbserverOutput.get() == null) origGdbserverOutput.set(gdbServerOutput.toString());
// Set the flag to retry the gdbserver launch
gdbserverLaunchRetry.set(true);
// Update the ports
gdbserverPortNumber.set(newPort);
gdbserverPortNumberMappedTo.set(newPortMappedTo);
}
}
}
gdbServerExited.set(true);
synchronized (lock) {
lock.notifyAll();
}
}
}
};
String peerName = null;
if (remotePID != null) {
peerName = peer.getName() + ", PID " + remotePID; //$NON-NLS-1$
}
// If there are commands to run before launching, create a script for them
String gdbserverLaunchCommand = gdbserverCommand + ' ' + commandArguments;
boolean launchAsRemoteUser = config.getAttribute(IRemoteTEConfigurationConstants.ATTR_LAUNCH_REMOTE_USER, false);
String userId = config.getAttribute(IRemoteTEConfigurationConstants.ATTR_REMOTE_USER_ID, (String)null);
String prelaunchCmd = config.getAttribute(IRemoteTEConfigurationConstants.ATTR_PRERUN_COMMANDS, ""); //$NON-NLS-1$
if (!isAttachLaunch &&
((prelaunchCmd != null && prelaunchCmd.trim().length() > 0) ||
(launchAsRemoteUser && userId != null && userId.trim().length() > 0))) {
if (prelaunchCmd == null) { prelaunchCmd = ""; } //$NON-NLS-1$
SimpleDateFormat formatter = new SimpleDateFormat ("HH-mm-ss-S", Locale.US); //$NON-NLS-1$
String prerunScriptNamePreffix = formatter.format( Long.valueOf(Calendar.getInstance().getTime().getTime()) );
String prerunScriptName = prerunScriptNamePreffix + "_" + exePath.toFile().getName() + ".sh"; //$NON-NLS-1$ //$NON-NLS-2$
IPath rootLocation = Activator.getDefault().getStateLocation().append("prerun_temp_scripts"); //$NON-NLS-1$
if (!rootLocation.toFile().exists()) rootLocation.toFile().mkdirs();
IPath prerunScriptLocation = rootLocation.append(prerunScriptName);
if (prerunScriptLocation.toFile().exists()) prerunScriptLocation.toFile().delete();
// Create the script
BufferedWriter writer = null;
try {
writer = new BufferedWriter(new FileWriter(prerunScriptLocation.toFile()));
writer.write(NLS.bind(TEHelper.getPrerunTemplateContent(peer), prelaunchCmd.replaceAll("\\r", ""), gdbserverLaunchCommand.replaceAll("\\r", ""))); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
} catch (Exception e) {
abort(NLS.bind(Messages.TEGdbAbstractLaunchDelegate_prerunScriptCreationFailed, prerunScriptLocation, e.getLocalizedMessage()), e, ICDTLaunchConfigurationConstants.ERR_PROGRAM_NOT_EXIST);
} finally {
if (writer != null) {
try {
writer.close();
} catch (IOException e) { /* Ignored on purpose. */ }
}
}
// Grant execution permission
prerunScriptLocation.toFile().setExecutable(true, false);
// Download the script to the target
IPath remotePrerunScriptPath = new Path("/tmp").append(prerunScriptName); //$NON-NLS-1$
try {
TEHelper.remoteFileTransfer(peer, prerunScriptLocation.toString(), remotePrerunScriptPath.toString(), new SubProgressMonitor(monitor, 80));
} catch (IOException e) {
abort(NLS.bind(Messages.TEGdbAbstractLaunchDelegate_prerunScriptTransferFailed, remotePrerunScriptPath.toString(), e.getLocalizedMessage()), e, ICDTLaunchConfigurationConstants.ERR_PROGRAM_NOT_EXIST);
} finally {
// Remove local temporal script
prerunScriptLocation.toFile().delete();
}
// Pass the user id as an argument to the script
String arguments = null;
if (launchAsRemoteUser && userId != null && userId.trim().length() > 0) {
arguments = "-u__ " + userId; //$NON-NLS-1$
}
launcher = TEHelper.launchCmdWithEnv(peer, peerName, remotePrerunScriptPath.toString(), arguments, commandEnv, listener, new SubProgressMonitor(monitor, 3), callback);
}
else {
String[] argv = StringUtil.tokenize(gdbserverLaunchCommand, 0, false);
launcher = TEHelper.launchCmdWithEnv(peer, peerName, argv[0], argv, commandEnv, listener, new SubProgressMonitor(monitor, 3), callback);
}
// Now wait until gdbserver is up and running on the remote host
while (!gdbServerReady.get() && !gdbServerExited.get()) {
if (monitor.isCanceled()) {
// gdbserver launch failed
// Need to shutdown the DSF launch session because it is
// partially started already.
shutdownSession(l, Messages.TEGdbAbstractLaunchDelegate_canceledMsg, launcher);
}
if (gdbServerStarted.get() && launcher.getChannel() == null) {
// gdbserver died or exited. Wait a little bit to process
// possible gdbserver output before shutting down the session.
synchronized (lock) {
try {
lock.wait(500);
}
catch (InterruptedException e) { /* ignored on purpose */ }
}
if (!gdbserverLaunchRetry.get()) {
shutdownSession(l, origGdbserverOutput.get() != null ? origGdbserverOutput.get() : gdbServerOutput.toString(), launcher);
}
}
synchronized (lock) {
try {
lock.wait(300);
}
catch (InterruptedException e) { /* ignored on purpose */ }
}
}
// If the gdbserver exited, also shutdown the DSF launch session
if (!gdbserverLaunchRetry.get() && gdbServerExited.get()) {
shutdownSession(l, origGdbserverOutput.get() != null ? origGdbserverOutput.get() : gdbServerOutput.toString(), launcher);
}
} while (gdbserverLaunchRetry.get());
// Set the launcher to the launch
if (launch instanceof TEGdbLaunch) {
((TEGdbLaunch)launch).setLauncher(launcher);
}
// Let debugger know how gdbserver was started on the remote
ILaunchConfigurationWorkingCopy wc = config.getWorkingCopy();
if (!origGdbserverPortNumber.equals(gdbserverPortNumber.get())) {
wc.setAttribute(IRemoteTEConfigurationConstants.ATTR_GDBSERVER_PORT, gdbserverPortNumber.get());
}
if (origGdbserverPortNumberMappedTo != null && !origGdbserverPortNumberMappedTo.equals(gdbserverPortNumberMappedTo.get())) {
wc.setAttribute(IRemoteTEConfigurationConstants.ATTR_GDBSERVER_PORT_MAPPED_TO, gdbserverPortNumberMappedTo.get());
}
wc.setAttribute(IGDBLaunchConfigurationConstants.ATTR_REMOTE_TCP, true);
wc.setAttribute(IGDBLaunchConfigurationConstants.ATTR_HOST, TEHelper.getCurrentConnection(config).getPeer().getAttributes().get(IPeer.ATTR_IP_HOST));
wc.setAttribute(IGDBLaunchConfigurationConstants.ATTR_PORT, gdbserverPortNumberMappedTo.get() == null || "".equals(gdbserverPortNumberMappedTo.get()) ? gdbserverPortNumber.get() : gdbserverPortNumberMappedTo.get()); //$NON-NLS-1$
// Expand GDB initialization file location (if set)
if (gdbinitFile != null) {
String projectName = config.getAttribute(ICDTLaunchConfigurationConstants.ATTR_PROJECT_NAME, (String)null);
String expanded = new DebugStringVariableSubstitutor(projectName).performStringSubstitution(gdbinitFile);
File f = new File(expanded);
wc.setAttribute(IGDBLaunchConfigurationConstants.ATTR_GDB_INIT, f.canRead() && f.isFile() ? f.getAbsolutePath() : null);
}
wc.doSave();
try {
super.launch(config, mode, launch, monitor);
}
catch (CoreException ex) {
// Launch failed, need to kill gdbserver
if (launcher != null) launcher.terminate();
// report failure further
throw ex;
}
finally {
monitor.done();
}
}
/* (non-Javadoc)
* @see org.eclipse.cdt.dsf.gdb.launching.GdbLaunchDelegate#launchDebugSession(org.eclipse.debug.core.ILaunchConfiguration, org.eclipse.debug.core.ILaunch, org.eclipse.core.runtime.IProgressMonitor)
*/
@Override
protected void launchDebugSession(ILaunchConfiguration config, ILaunch l, IProgressMonitor monitor) throws CoreException {
super.launchDebugSession(config, l, monitor);
addWindowsBatchJobTerminator(l);
// Determine if the launch is an attach launch
final boolean isAttachLaunch = ICDTLaunchConfigurationConstants.ID_LAUNCH_C_ATTACH.equals(config.getType().getIdentifier());
if (!isAttachLaunch)
return;
boolean ok = false;
try {
if (!(l instanceof GdbLaunch))
throw new DebugException(new Status(IStatus.ERROR, Activator.getUniqueIdentifier(), "Unexpected launch: " + l.getClass().getName())); //$NON-NLS-1$
final IPath exePath = checkBinaryDetails(config);
if (exePath == null)
throw new DebugException(new Status(IStatus.ERROR, Activator.getUniqueIdentifier(), "No executable specified")); //$NON-NLS-1$
final GdbLaunch launch = (GdbLaunch) l;
final DsfExecutor executor = launch.getDsfExecutor();
final String pid = config.getAttribute(IRemoteTEConfigurationConstants.ATTR_REMOTE_PID, "0"); //$NON-NLS-1$
Query<IDMContext> query = new Query<IDMContext>() {
@Override
protected void execute(DataRequestMonitor<IDMContext> rm) {
DsfServicesTracker tracker = new DsfServicesTracker(Activator.getDefault().getBundle().getBundleContext(), launch.getSession().getId());
try {
IGDBProcesses gdbProcesses = tracker.getService(IGDBProcesses.class);
IGDBControl commandControl = tracker.getService(IGDBControl.class);
gdbProcesses.attachDebuggerToProcess(
gdbProcesses.createProcessContext(commandControl.getContext(), pid),
exePath.toString(),
rm);
} finally {
tracker.dispose();
}
}
};
executor.execute(query);
if (query.get() != null)
ok = true;
} catch (InterruptedException e) {
} catch (ExecutionException e) {
throw new DebugException(new Status(IStatus.ERROR, Activator.getUniqueIdentifier(), "Cannot attach to process", e)); //$NON-NLS-1$
} finally {
if (!ok) {
cleanupLaunchLocal(l);
}
}
}
/**
* @deprecated While waiting for TCF to drop support for CDT < 9.0 we need this method copied
* from CDT 9.0. Once support for old CDT is removed this should become simply
* super.cleanupLaunch(ILaunch).
*/
@Deprecated
protected void cleanupLaunchLocal(ILaunch launch) throws DebugException {
if (launch instanceof GdbLaunch) {
final GdbLaunch gdbLaunch = (GdbLaunch)launch;
Query<Object> launchShutdownQuery = new Query<Object>() {
@Override
protected void execute(DataRequestMonitor<Object> rm) {
gdbLaunch.shutdownSession(rm);
}
};
gdbLaunch.getSession().getExecutor().execute(launchShutdownQuery);
// wait for the shutdown to finish.
// The Query.get() method is a synchronous call which blocks until the
// query completes.
try {
launchShutdownQuery.get();
} catch (InterruptedException e) {
throw new DebugException( new Status(IStatus.ERROR, Activator.getUniqueIdentifier(), DebugException.INTERNAL_ERROR, "InterruptedException while shutting down debugger launch " + launch, e)); //$NON-NLS-1$
} catch (ExecutionException e) {
throw new DebugException(new Status(IStatus.ERROR, Activator.getUniqueIdentifier(), DebugException.REQUEST_FAILED, "Error in shutting down debugger launch " + launch, e)); //$NON-NLS-1$
}
}
}
/**
* On Windows, if gdb was started as part of a batch script, the script does not exit silently
* if it has been interrupted before. Therefore we listen for the completion of the -gdb-exit command
* and terminate the backend by force if necessary.
*
* @param launch
*/
private void addWindowsBatchJobTerminator(ILaunch launch) {
if (Host.isWindowsHost() && launch instanceof GdbLaunch) {
final DsfSession s = ((GdbLaunch) launch).getSession();
s.getExecutor().execute(new Runnable() {
@Override
public void run() {
DsfServicesTracker tracker = new DsfServicesTracker(Activator.getDefault().getBundle().getBundleContext(), s.getId());
try {
final IGDBBackend backend = tracker.getService(IGDBBackend.class);
final IGDBControl commandControl = tracker.getService(IGDBControl.class);
commandControl.addCommandListener(new ICommandListener() {
@Override
public void commandSent(ICommandToken token) {
}
@Override
public void commandRemoved(ICommandToken token) {
}
@Override
public void commandQueued(ICommandToken token) {
}
@Override
public void commandDone(ICommandToken token, ICommandResult result) {
if (token.getCommand() instanceof MIGDBExit) {
s.getExecutor().schedule(new Callable<Object>() {
@Override
public Object call() throws Exception {
if (commandControl.isActive() && backend.getState() != IMIBackend.State.TERMINATED)
backend.destroy();
return null;
}
}, 500, TimeUnit.MILLISECONDS);
}
}
});
} finally {
tracker.dispose();
}
}
});
}
}
/**
* Shutdown the GDB debug session.
* This method always throws a CoreException and does not return.
*
* @param launch The GDB launch. Must not be <code>null</code>.
* @param details Error message, may be <code>null</code>
* @throws CoreException Always throws a CoreException
*/
protected void shutdownSession(final GdbLaunch launch, final String details, final ProcessLauncher launcher) throws CoreException {
Assert.isNotNull(launch);
try {
launch.getSession().getExecutor().submit(new DsfRunnable() {
@Override
public void run() {
// Avoid an NPE while running the shutdown
if (launch.getDsfExecutor() != null) {
launch.shutdownSession(new ImmediateRequestMonitor());
}
// Make sure that the gdbserver is killed and the launcher resources gets disposed
if (launcher != null) launcher.terminate();
}
}).get(1000, TimeUnit.MILLISECONDS);
}
catch (RejectedExecutionException e) {
// Session disposed.
}
catch (Exception e) {
// Ignore exceptions during shutdown.
}
// Normalize the gdbserver start failure details
String details2 = normalizeDetails(launch, details);
String msg = Messages.TEGdbAbstractLaunchDelegate_gdbserverFailedToStartErrorMessage;
if (details2 != null && details2.length() > 0) msg = NLS.bind(Messages.TEGdbAbstractLaunchDelegate_gdbserverFailedToStartErrorWithDetails, details2);
abort(msg, null, ICDTLaunchConfigurationConstants.ERR_DEBUGGER_NOT_INSTALLED);
}
/**
* Normalize the gdbserver launch failure details message.
*
* @param launch The launch. Must not be <code>null</code>
* @param details The details message or <code>null</code>.
*
* @return The normalized details message or <code>null</code>.
*
* @throws CoreException In case of an failure accessing any launch configuration attribute or similar.
*/
protected String normalizeDetails(final GdbLaunch launch, final String details) throws CoreException {
Assert.isNotNull(launch);
// Get the launch configuration from the launch
final ILaunchConfiguration lc = launch.getLaunchConfiguration();
final boolean isAttachLaunch = ICDTLaunchConfigurationConstants.ID_LAUNCH_C_ATTACH.equals(lc.getType().getIdentifier());
String d = details;
if (d != null && !"".equals(d)) { //$NON-NLS-1$
// Try the delegate first if available
IPeerNode peerNode = TEHelper.getCurrentConnection(lc);
Assert.isNotNull(peerNode);
IGdbserverLaunchHandlerDelegate delegate = ServiceUtils.getDelegateServiceDelegate(peerNode, peerNode, IGdbserverLaunchHandlerDelegate.class);
if (delegate != null) {
d = delegate.normalizeGdbserverLaunchFailureDetailsMessage(launch, details);
} else {
// Rewrite "Address in use" error.
if (d.contains("Address already in use.")) { //$NON-NLS-1$
// Get host and port
String host = lc.getAttribute(IGDBLaunchConfigurationConstants.ATTR_HOST, (String)null);
String port = fCurrentGdbServerPort;
String address = host + (port != null ? ":" + port : ""); //$NON-NLS-1$ //$NON-NLS-2$
d = NLS.bind(Messages.TEGdbAbstractLaunchDelegate_error_addressInUse, address);
}
// Rewrite "No such file or directory" error.
else if (d.contains("No such file or directory")) { //$NON-NLS-1$
// Get gdbserver path
String gdbserverCommand = lc.getAttribute(IRemoteTEConfigurationConstants.ATTR_GDBSERVER_COMMAND, TEHelper.getStringPreferenceValue(isAttachLaunch ? IPreferenceKeys.PREF_GDBSERVER_COMMAND_ATTACH : IPreferenceKeys.PREF_GDBSERVER_COMMAND));
d = NLS.bind(Messages.TEGdbAbstractLaunchDelegate_error_nosuchfileordirectory, gdbserverCommand);
}
}
}
return d;
}
protected String getProgramArguments(ILaunchConfiguration config) throws CoreException {
String args = config.getAttribute(ICDTLaunchConfigurationConstants.ATTR_PROGRAM_ARGUMENTS, (String) null);
if (args != null) {
args = VariablesPlugin.getDefault().getStringVariableManager().performStringSubstitution(args);
}
return args;
}
/* (non-Javadoc)
* @see org.eclipse.cdt.dsf.gdb.launching.GdbLaunchDelegate#getPluginID()
*/
@Override
protected String getPluginID() {
return Activator.getUniqueIdentifier();
}
}