From 65956e57785a4d8af3061760061104c545fb277d Mon Sep 17 00:00:00 2001 From: Uwe Stieber Date: Mon, 25 May 2015 16:27:00 +0200 Subject: Target Explorer: Add auto-port capability for gdbserver launch for CDT based launches --- .../IGdbserverLaunchHandlerDelegate.java | 31 +++ .../cdt/launching/TEGdbAbstractLaunchDelegate.java | 302 ++++++++++++++------- .../te/tcf/launch/cdt/launching/TEGdbLaunch.java | 69 +++++ .../tcf/te/tcf/launch/cdt/nls/Messages.java | 1 + .../tcf/te/tcf/launch/cdt/nls/Messages.properties | 4 + .../tcf/te/tcf/launch/cdt/utils/TEHelper.java | 90 ++---- .../processes/core/launcher/ProcessLauncher.java | 7 +- 7 files changed, 347 insertions(+), 157 deletions(-) create mode 100644 target_explorer/plugins/org.eclipse.tcf.te.tcf.launch.cdt/src/org/eclipse/tcf/te/tcf/launch/cdt/interfaces/IGdbserverLaunchHandlerDelegate.java create mode 100644 target_explorer/plugins/org.eclipse.tcf.te.tcf.launch.cdt/src/org/eclipse/tcf/te/tcf/launch/cdt/launching/TEGdbLaunch.java (limited to 'target_explorer') diff --git a/target_explorer/plugins/org.eclipse.tcf.te.tcf.launch.cdt/src/org/eclipse/tcf/te/tcf/launch/cdt/interfaces/IGdbserverLaunchHandlerDelegate.java b/target_explorer/plugins/org.eclipse.tcf.te.tcf.launch.cdt/src/org/eclipse/tcf/te/tcf/launch/cdt/interfaces/IGdbserverLaunchHandlerDelegate.java new file mode 100644 index 000000000..e5419dfc6 --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tcf.te.tcf.launch.cdt/src/org/eclipse/tcf/te/tcf/launch/cdt/interfaces/IGdbserverLaunchHandlerDelegate.java @@ -0,0 +1,31 @@ +/******************************************************************************* + * Copyright (c) 2015 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.interfaces; + +import org.eclipse.cdt.dsf.gdb.launching.GdbLaunch; +import org.eclipse.core.runtime.CoreException; + +/** + * Gdbserver launch delegate interface. + */ +public interface IGdbserverLaunchHandlerDelegate { + + /** + * Normalize the gdbserver launch failure details message. + * + * @param launch The launch. Must not be null + * @param details The details message or null. + * + * @return The normalized details message or null. + * + * @throws CoreException In case of an failure accessing any launch configuration attribute or similar. + */ + public String normalizeGdbserverLaunchFailureDetailsMessage( GdbLaunch launch, String details) throws CoreException; +} diff --git a/target_explorer/plugins/org.eclipse.tcf.te.tcf.launch.cdt/src/org/eclipse/tcf/te/tcf/launch/cdt/launching/TEGdbAbstractLaunchDelegate.java b/target_explorer/plugins/org.eclipse.tcf.te.tcf.launch.cdt/src/org/eclipse/tcf/te/tcf/launch/cdt/launching/TEGdbAbstractLaunchDelegate.java index 674aec519..4f7818731 100644 --- a/target_explorer/plugins/org.eclipse.tcf.te.tcf.launch.cdt/src/org/eclipse/tcf/te/tcf/launch/cdt/launching/TEGdbAbstractLaunchDelegate.java +++ b/target_explorer/plugins/org.eclipse.tcf.te.tcf.launch.cdt/src/org/eclipse/tcf/te/tcf/launch/cdt/launching/TEGdbAbstractLaunchDelegate.java @@ -10,9 +10,12 @@ package org.eclipse.tcf.te.tcf.launch.cdt.launching; import java.io.IOException; +import java.util.List; 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.dsf.concurrent.DsfExecutor; @@ -37,14 +40,18 @@ import org.eclipse.core.variables.VariablesPlugin; import org.eclipse.debug.core.ILaunch; import org.eclipse.debug.core.ILaunchConfiguration; import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; +import org.eclipse.debug.core.model.ISourceLocator; import org.eclipse.osgi.util.NLS; import org.eclipse.tcf.protocol.IPeer; import org.eclipse.tcf.te.runtime.callback.Callback; +import org.eclipse.tcf.te.runtime.services.ServiceUtils; 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.utils.TEHelper; +import org.eclipse.tcf.te.tcf.locator.interfaces.nodes.IPeerNode; import org.eclipse.tcf.te.tcf.processes.core.launcher.ProcessLauncher; /** @@ -69,6 +76,14 @@ public abstract class TEGdbAbstractLaunchDelegate extends GdbLaunchDelegate { 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) */ @@ -119,113 +134,182 @@ public abstract class TEGdbAbstractLaunchDelegate extends GdbLaunchDelegate { } // Launch gdbserver on target - String gdbserverPortNumber = config.getAttribute(IRemoteTEConfigurationConstants.ATTR_GDBSERVER_PORT, IRemoteTEConfigurationConstants.ATTR_GDBSERVER_PORT_DEFAULT); - String gdbserverPortNumberMappedTo = config.getAttribute(IRemoteTEConfigurationConstants.ATTR_GDBSERVER_PORT_MAPPED_TO, (String) null); - String gdbserverCommand = config.getAttribute(IRemoteTEConfigurationConstants.ATTR_GDBSERVER_COMMAND, IRemoteTEConfigurationConstants.ATTR_GDBSERVER_COMMAND_DEFAULT); - - String commandArguments = ""; //$NON-NLS-1$ - if (isAttachLaunch) { - commandArguments = "--attach :" + gdbserverPortNumber + " " + remotePID; //$NON-NLS-1$ //$NON-NLS-2$ - monitor.setTaskName(Messages.TEGdbAbstractLaunchDelegate_attaching_program); - } else { - commandArguments = ":" + gdbserverPortNumber + " " + TEHelper.spaceEscapify(remoteExePath); //$NON-NLS-1$ //$NON-NLS-2$ - - String arguments = getProgramArguments(config); - String prelaunchCmd = config.getAttribute(IRemoteTEConfigurationConstants.ATTR_PRERUN_COMMANDS, ""); //$NON-NLS-1$ - - TEHelper.launchCmd(peer, prelaunchCmd, null, new SubProgressMonitor(monitor, 2), new Callback()); - - if (arguments != null && !arguments.equals("")) { //$NON-NLS-1$ - commandArguments += " " + arguments; //$NON-NLS-1$ + final AtomicReference gdbserverPortNumber = new AtomicReference(config.getAttribute(IRemoteTEConfigurationConstants.ATTR_GDBSERVER_PORT, IRemoteTEConfigurationConstants.ATTR_GDBSERVER_PORT_DEFAULT)); + final AtomicReference gdbserverPortNumberMappedTo = new AtomicReference(config.getAttribute(IRemoteTEConfigurationConstants.ATTR_GDBSERVER_PORT_MAPPED_TO, (String) null)); + final String gdbserverCommand = config.getAttribute(IRemoteTEConfigurationConstants.ATTR_GDBSERVER_COMMAND, IRemoteTEConfigurationConstants.ATTR_GDBSERVER_COMMAND_DEFAULT); + final List gdbserverPortNumberAlternatives = config.getAttribute(IRemoteTEConfigurationConstants.ATTR_GDBSERVER_PORT_ALTERNATIVES, (List) null); + final List gdbserverPortNumberMappedToAlternatives = config.getAttribute(IRemoteTEConfigurationConstants.ATTR_GDBSERVER_PORT_MAPPED_TO_ALTERNATIVES, (List) null); + +// For quick testing. +// final List gdbserverPortNumberAlternatives = new ArrayList(Arrays.asList(new String[] { "49152", "49153", "49154", "49155", "49156" })); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ +// final List gdbserverPortNumberMappedToAlternatives = new ArrayList(Arrays.asList(new String[] { "49652", "49653", "49654", "49655", "49656" })); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ + + // Remember the originally configured port number and mapped to port number + final String origGdbserverPortNumber = gdbserverPortNumber.get(); + final String origGdbserverPortNumberMappedTo = gdbserverPortNumberMappedTo.get(); + final AtomicReference origGdbserverOutput = new AtomicReference(null); + + ProcessLauncher launcher = null; + + final AtomicBoolean gdbserverLaunchRetry = new AtomicBoolean(false); +// final AtomicBoolean portRangeExceeded = new AtomicBoolean(false); + final AtomicInteger indexAlternatives = new AtomicInteger(0); + + do { + 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$ + if (isAttachLaunch) { + commandArguments = "--attach :" + gdbserverPortNumber.get() + " " + remotePID; //$NON-NLS-1$ //$NON-NLS-2$ + monitor.setTaskName(Messages.TEGdbAbstractLaunchDelegate_attaching_program); + } else { + commandArguments = ":" + gdbserverPortNumber.get() + " " + TEHelper.spaceEscapify(remoteExePath); //$NON-NLS-1$ //$NON-NLS-2$ + + String arguments = getProgramArguments(config); + String prelaunchCmd = config.getAttribute(IRemoteTEConfigurationConstants.ATTR_PRERUN_COMMANDS, ""); //$NON-NLS-1$ + + TEHelper.launchCmd(peer, prelaunchCmd, null, new SubProgressMonitor(monitor, 2), new Callback()); + + if (arguments != null && !arguments.equals("")) { //$NON-NLS-1$ + commandArguments += " " + arguments; //$NON-NLS-1$ + } + monitor.setTaskName(Messages.TEGdbAbstractLaunchDelegate_starting_program); } - monitor.setTaskName(Messages.TEGdbAbstractLaunchDelegate_starting_program); - } - final AtomicBoolean gdbServerStarted = new AtomicBoolean(false); - final AtomicBoolean gdbServerReady = new AtomicBoolean(false); - final AtomicBoolean gdbServerExited = new AtomicBoolean(false); - final StringBuffer gdbServerOutput = new StringBuffer(); - final Object lock = new Object(); - - 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(); + 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); } - else { - gdbServerStarted.set(true); + }; + + 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.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(); + } + } + } - super.internalDone(caller, status); - } - }; + }; - StreamsDataReceiver.Listener listener = new StreamsDataReceiver.Listener() { + launcher = TEHelper.launchCmd(peer, gdbserverCommand, commandArguments, listener, new SubProgressMonitor(monitor, 3), callback); - @Override - public void dataReceived(String data) { - gdbServerOutput.append(data); - if (data.contains("Listening on port")) { //$NON-NLS-1$ - gdbServerReady.set(true); + // 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) { - lock.notifyAll(); + try { + lock.wait(500); + } + catch (InterruptedException e) { /* ignored on purpose */ } + } + if (!gdbserverLaunchRetry.get()) { + shutdownSession(l, origGdbserverOutput.get() != null ? origGdbserverOutput.get() : gdbServerOutput.toString(), launcher); } } - else if (data.contains("GDBserver exiting") || data.contains("Exiting")) { //$NON-NLS-1$ //$NON-NLS-2$ - gdbServerExited.set(true); - synchronized (lock) { - lock.notifyAll(); + synchronized (lock) { + try { + lock.wait(300); } + catch (InterruptedException e) { /* ignored on purpose */ } } - } - }; - - ProcessLauncher launcher = TEHelper.launchCmd(peer, gdbserverCommand, commandArguments, 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); + // 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); } - if (gdbServerStarted.get() && launcher.getChannel() == null) { - // gdbserver died - shutdownSession(l, gdbServerOutput.toString()); - } - synchronized (lock) { - try { - lock.wait(300); - } - catch (InterruptedException e) { - } - } - } + } while (gdbserverLaunchRetry.get()); - // If the gdbserver exited, also shutdown the DSF launch session - if (gdbServerExited.get()) { - shutdownSession(l, gdbServerOutput.toString()); + // 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 == null || "".equals(gdbserverPortNumberMappedTo) ? gdbserverPortNumber : gdbserverPortNumberMappedTo); //$NON-NLS-1$ + wc.setAttribute(IGDBLaunchConfigurationConstants.ATTR_PORT, gdbserverPortNumberMappedTo.get() == null || "".equals(gdbserverPortNumberMappedTo.get()) ? gdbserverPortNumber.get() : gdbserverPortNumberMappedTo.get()); //$NON-NLS-1$ wc.doSave(); try { super.launch(config, mode, launch, monitor); } catch (CoreException ex) { // Launch failed, need to kill gdbserver - launcher.terminate(); + if (launcher != null) launcher.terminate(); // report failure further throw ex; } @@ -234,7 +318,6 @@ public abstract class TEGdbAbstractLaunchDelegate extends GdbLaunchDelegate { } } - /* (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) */ @@ -273,16 +356,6 @@ public abstract class TEGdbAbstractLaunchDelegate extends GdbLaunchDelegate { } } - /** - * Shutdown the GDB debug session. - * - * @param launch The GDB launch. Must not be null. - * @throws CoreException If the GDB debug session shutdown failed. - */ - protected void shutdownSession(final GdbLaunch launch) throws CoreException { - shutdownSession(launch, null); - } - /** * Shutdown the GDB debug session. * @@ -290,7 +363,7 @@ public abstract class TEGdbAbstractLaunchDelegate extends GdbLaunchDelegate { * @param details Error message, may be null * @throws CoreException If the GDB debug session shutdown failed. */ - protected void shutdownSession(final GdbLaunch launch, String details) throws 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() { @@ -300,6 +373,8 @@ public abstract class TEGdbAbstractLaunchDelegate extends GdbLaunchDelegate { 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); } @@ -310,11 +385,54 @@ public abstract class TEGdbAbstractLaunchDelegate extends GdbLaunchDelegate { // Ignore exceptions during shutdown. } + // Normalize the gdbserver start failure details + String details2 = normalizeDetails(launch, details); + String msg = Messages.TEGdbAbstractLaunchDelegate_gdbserverFailedToStartErrorMessage; - if (details != null && details.length() > 0) msg = NLS.bind(Messages.TEGdbAbstractLaunchDelegate_gdbserverFailedToStartErrorWithDetails, details); + 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 null + * @param details The details message or null. + * + * @return The normalized details message or null. + * + * @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(); + + 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 = lc.getAttribute(IGDBLaunchConfigurationConstants.ATTR_PORT, (String)null); + + String address = host + (port != null ? ":" + port : ""); //$NON-NLS-1$ //$NON-NLS-2$ + d = NLS.bind(Messages.TEGdbAbstractLaunchDelegate_error_addressInUse, address); + } + } + } + + return d; + } + protected String getProgramArguments(ILaunchConfiguration config) throws CoreException { String args = config.getAttribute(ICDTLaunchConfigurationConstants.ATTR_PROGRAM_ARGUMENTS, (String) null); if (args != null) { diff --git a/target_explorer/plugins/org.eclipse.tcf.te.tcf.launch.cdt/src/org/eclipse/tcf/te/tcf/launch/cdt/launching/TEGdbLaunch.java b/target_explorer/plugins/org.eclipse.tcf.te.tcf.launch.cdt/src/org/eclipse/tcf/te/tcf/launch/cdt/launching/TEGdbLaunch.java new file mode 100644 index 000000000..909b0c14b --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tcf.te.tcf.launch.cdt/src/org/eclipse/tcf/te/tcf/launch/cdt/launching/TEGdbLaunch.java @@ -0,0 +1,69 @@ +/******************************************************************************* + * Copyright (c) 2015 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 org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.gdb.launching.GdbLaunch; +import org.eclipse.debug.core.ILaunchConfiguration; +import org.eclipse.debug.core.model.ISourceLocator; +import org.eclipse.tcf.te.tcf.processes.core.launcher.ProcessLauncher; + +/** + * GDB launch implementation handling also the gdbserver life cycle. + */ +public class TEGdbLaunch extends GdbLaunch { + // The process launcher used to launch the gdbserver process + private ProcessLauncher launcher = null; + + /** + * Constructor + * + * @param launchConfiguration The launch configuration. Must not be null. + * @param mode The launch mode. Must not be null. + * @param locator The source locator to use for this debug session, or null if not supported + */ + public TEGdbLaunch(ILaunchConfiguration launchConfiguration, String mode, ISourceLocator locator) { + super(launchConfiguration, mode, locator); + } + + /** + * Sets the process launcher. + * + * @param launcher The process launcher or null. + */ + public final void setLauncher(ProcessLauncher launcher) { + this.launcher = launcher; + } + + /** + * Returns the process launcher. + * + * @return The process launcher or null. + */ + public final ProcessLauncher getLauncher() { + return launcher; + } + + /* (non-Javadoc) + * @see org.eclipse.cdt.dsf.gdb.launching.GdbLaunch#shutdownSession(org.eclipse.cdt.dsf.concurrent.RequestMonitor) + */ + @Override + public void shutdownSession(RequestMonitor rm) { + RequestMonitor r = new RequestMonitor(getDsfExecutor(), rm) { + @Override + protected void handleCompleted() { + ProcessLauncher launcher = getLauncher(); + if (launcher != null) launcher.terminate(); + super.handleCompleted(); + } + }; + super.shutdownSession(r); + } +} diff --git a/target_explorer/plugins/org.eclipse.tcf.te.tcf.launch.cdt/src/org/eclipse/tcf/te/tcf/launch/cdt/nls/Messages.java b/target_explorer/plugins/org.eclipse.tcf.te.tcf.launch.cdt/src/org/eclipse/tcf/te/tcf/launch/cdt/nls/Messages.java index 08617f0e2..c06c4cf43 100644 --- a/target_explorer/plugins/org.eclipse.tcf.te.tcf.launch.cdt/src/org/eclipse/tcf/te/tcf/launch/cdt/nls/Messages.java +++ b/target_explorer/plugins/org.eclipse.tcf.te.tcf.launch.cdt/src/org/eclipse/tcf/te/tcf/launch/cdt/nls/Messages.java @@ -53,6 +53,7 @@ public class Messages extends NLS { public static String TEGdbAbstractLaunchDelegate_filetransferFailed; public static String TEGdbAbstractLaunchDelegate_gdbserverFailedToStartErrorMessage; public static String TEGdbAbstractLaunchDelegate_gdbserverFailedToStartErrorWithDetails; + public static String TEGdbAbstractLaunchDelegate_error_addressInUse; static { // initialize resource bundle diff --git a/target_explorer/plugins/org.eclipse.tcf.te.tcf.launch.cdt/src/org/eclipse/tcf/te/tcf/launch/cdt/nls/Messages.properties b/target_explorer/plugins/org.eclipse.tcf.te.tcf.launch.cdt/src/org/eclipse/tcf/te/tcf/launch/cdt/nls/Messages.properties index 71fc685ff..032d824c2 100644 --- a/target_explorer/plugins/org.eclipse.tcf.te.tcf.launch.cdt/src/org/eclipse/tcf/te/tcf/launch/cdt/nls/Messages.properties +++ b/target_explorer/plugins/org.eclipse.tcf.te.tcf.launch.cdt/src/org/eclipse/tcf/te/tcf/launch/cdt/nls/Messages.properties @@ -47,6 +47,10 @@ TEGdbAbstractLaunchDelegate_starting_debugger=Starting Debugger TEGdbAbstractLaunchDelegate_filetransferFailed=Failed to download application image to target. Possibly caused by: {0} TEGdbAbstractLaunchDelegate_gdbserverFailedToStartErrorMessage=Could not start gdbserver on the remote host. See console output for more details. TEGdbAbstractLaunchDelegate_gdbserverFailedToStartErrorWithDetails=Could not start gdbserver on the remote host. Possibly caused by:\n{0} +TEGdbAbstractLaunchDelegate_error_addressInUse=The address {0} is already in use on the remote host. Possibly another gdbserver process is already \ +running using the same port to listen for connections.\n\nPlease configure another port to use within the launch configuration. Do do so, open the \ +launch configuration dialog, select the launch configuration and switch to the "Debugger" tab. On the "Debugger" tab, configure the port to use on \ +the "gdbserver" tab. TCFPeerSelector_0=Connection: diff --git a/target_explorer/plugins/org.eclipse.tcf.te.tcf.launch.cdt/src/org/eclipse/tcf/te/tcf/launch/cdt/utils/TEHelper.java b/target_explorer/plugins/org.eclipse.tcf.te.tcf.launch.cdt/src/org/eclipse/tcf/te/tcf/launch/cdt/utils/TEHelper.java index ae2c0f42a..f225d140b 100644 --- a/target_explorer/plugins/org.eclipse.tcf.te.tcf.launch.cdt/src/org/eclipse/tcf/te/tcf/launch/cdt/utils/TEHelper.java +++ b/target_explorer/plugins/org.eclipse.tcf.te.tcf.launch.cdt/src/org/eclipse/tcf/te/tcf/launch/cdt/utils/TEHelper.java @@ -39,7 +39,6 @@ import org.eclipse.tcf.te.runtime.callback.Callback; import org.eclipse.tcf.te.runtime.concurrent.util.ExecutorsUtil; import org.eclipse.tcf.te.runtime.interfaces.callback.ICallback; import org.eclipse.tcf.te.runtime.interfaces.properties.IPropertiesContainer; -import org.eclipse.tcf.te.runtime.processes.ProcessOutputReaderThread; import org.eclipse.tcf.te.runtime.properties.PropertiesContainer; import org.eclipse.tcf.te.runtime.services.filetransfer.FileTransferItem; import org.eclipse.tcf.te.runtime.services.interfaces.filetransfer.IFileTransferItem; @@ -198,6 +197,33 @@ public class TEHelper { Map launchAttributes = new HashMap(); + // Compute the terminal title if possible + if (args != null && args.length > 0) { + StringBuilder title = new StringBuilder(); + IPath p = new Path(remoteCommandPath); + // Avoid very long terminal title's by shortening the path if it has more than 3 segments + if (p.segmentCount() > 3) { + title.append(".../"); //$NON-NLS-1$ + title.append(p.lastSegment()); + } else { + title.append(p.toString()); + } + + for (String arg : args) { + if (arg.matches(":[0-9]+")) { //$NON-NLS-1$ + title.append(arg); + break; + } + } + + String name = peer.getName(); + if (name != null && !"".equals(name)) { //$NON-NLS-1$ + title.append(" [" + name + "]"); //$NON-NLS-1$ //$NON-NLS-2$ + } + + if (title.length() > 0) launchAttributes.put(ITerminalsConnectorConstants.PROP_TITLE, title.toString()); + } + launchAttributes.put(IProcessLauncher.PROP_PROCESS_PATH, spaceEscapify(remoteCommandPath)); launchAttributes.put(IProcessLauncher.PROP_PROCESS_ARGS, args); @@ -222,10 +248,8 @@ public class TEHelper { Runnable runnable = new Runnable() { @Override public void run() { - if (ITransportTypes.TRANSPORT_TYPE_TCP.equals(peer.getTransportName()) || ITransportTypes.TRANSPORT_TYPE_SSL - .equals(peer.getTransportName())) { - isLocalhost.set(IPAddressUtil.getInstance().isLocalHost(peer - .getAttributes().get(IPeer.ATTR_IP_HOST))); + if (ITransportTypes.TRANSPORT_TYPE_TCP.equals(peer.getTransportName()) || ITransportTypes.TRANSPORT_TYPE_SSL.equals(peer.getTransportName())) { + isLocalhost.set(IPAddressUtil.getInstance().isLocalHost(peer.getAttributes().get(IPeer.ATTR_IP_HOST))); } } }; @@ -257,62 +281,6 @@ public class TEHelper { return null; } - public static String launchCmdReadOutput(final IPeer peer, String remoteCommandPath, String[] args, final SubProgressMonitor monitor, ICallback callback) throws CoreException { - String output = null; - if (remoteCommandPath != null && !remoteCommandPath.trim().equals("")) { //$NON-NLS-1$ - monitor.beginTask(NLS.bind(Messages.TEHelper_executing, remoteCommandPath, args), 10); - - // Construct the launcher object - final ProcessStreamsProxy proxy = new ProcessStreamsProxy(); - ProcessLauncher launcher = new ProcessLauncher(proxy); - - Map launchAttributes = new HashMap(); - - launchAttributes.put(IProcessLauncher.PROP_PROCESS_PATH, spaceEscapify(remoteCommandPath)); - launchAttributes.put(IProcessLauncher.PROP_PROCESS_ARGS, args); - - launchAttributes.put(ITerminalsConnectorConstants.PROP_LOCAL_ECHO, Boolean.FALSE); - launchAttributes.put(IProcessLauncher.PROP_PROCESS_ASSOCIATE_CONSOLE, Boolean.TRUE); - - // Fill in the launch attributes - IPropertiesContainer container = new PropertiesContainer(); - container.setProperties(launchAttributes); - final boolean processDone[] = new boolean[1]; - processDone[0] = false; - - // Launch the process - launcher.launch(peer, container, new Callback(callback) { - @Override - protected void internalDone(Object caller, IStatus status) { - super.internalDone(caller, status); - processDone[0] = true; - } - }); - - final Object lock = new Object(); - - synchronized (lock) { - while (processDone[0] == false) { - if (monitor.isCanceled()) { - break; - } - } - try { - lock.wait(300); - } - catch (InterruptedException e) { - } - } - ProcessOutputReaderThread reader = proxy.getOutputReader(); - while (!reader.isFinished()) { - reader.waitForFinish(); - } - output = reader.getOutput(); - } - monitor.done(); - return output; - } - /** * Throws a core exception with an error status object built from the given message, lower level * exception, and error code. diff --git a/target_explorer/plugins/org.eclipse.tcf.te.tcf.processes.core/src/org/eclipse/tcf/te/tcf/processes/core/launcher/ProcessLauncher.java b/target_explorer/plugins/org.eclipse.tcf.te.tcf.processes.core/src/org/eclipse/tcf/te/tcf/processes/core/launcher/ProcessLauncher.java index 7ffd71e5c..bde2d8206 100644 --- a/target_explorer/plugins/org.eclipse.tcf.te.tcf.processes.core/src/org/eclipse/tcf/te/tcf/processes/core/launcher/ProcessLauncher.java +++ b/target_explorer/plugins/org.eclipse.tcf.te.tcf.processes.core/src/org/eclipse/tcf/te/tcf/processes/core/launcher/ProcessLauncher.java @@ -597,10 +597,9 @@ public class ProcessLauncher extends PlatformObject implements IProcessLauncher props.put(ITerminalsConnectorConstants.PROP_DELEGATE_ID, "org.eclipse.tm.terminal.connector.streams.launcher.streams"); //$NON-NLS-1$ props.put(ITerminalsConnectorConstants.PROP_ID, "org.eclipse.tm.terminal.view.ui.TerminalsView"); //$NON-NLS-1$ // Set the terminal tab title - String terminalTitle = getTerminalTitle(); - if (terminalTitle != null) { - props.put(ITerminalsConnectorConstants.PROP_TITLE, terminalTitle); - } + String terminalTitle = properties.getStringProperty(ITerminalsConnectorConstants.PROP_TITLE); + if (terminalTitle == null) terminalTitle = getTerminalTitle(); + if (terminalTitle != null) props.put(ITerminalsConnectorConstants.PROP_TITLE, terminalTitle); // Get the process output listener list from the properties Object value = properties.getProperty(PROP_PROCESS_OUTPUT_LISTENER); -- cgit v1.2.3