1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-07-04 07:35:24 +02:00

Apply patch for bug 157426 - Remote CDT Launch Improvements by Ewa M

This commit is contained in:
Martin Oberhuber 2006-09-19 21:31:22 +00:00
parent 53d4619a2e
commit e9a568db01
11 changed files with 396 additions and 193 deletions

View file

@ -23,6 +23,7 @@ Require-Bundle: org.eclipse.core.runtime,
org.eclipse.rse.services,
org.eclipse.cdt.debug.core,
org.eclipse.cdt.debug.mi.ui,
org.eclipse.rse.core
org.eclipse.rse.core,
org.eclipse.rse.subsystems.files.core
Eclipse-LazyStart: true
Bundle-Vendor: Eclipse.org

View file

@ -1,17 +1,26 @@
Introduction
--------------
The attached plugin (org.eclipse.rse.remotecdt) builds on top of CDT and integrates with RSE to introduce a "Remote C++ Application" launch configuration (see screenshot1 attached). This launch configuration shows RSE connections of type "Ssh\Gdbserver" in a pulldown. The "Ssh\Gdbserver" connection type is introduced to RSE with this plugin (screenshot2). The host details from that selected connection are used to establish an ssh/sftp connection to the target. For a debug configuration, the binary is downloaded to the remote (using sftp), gdbserver is automatically started on the remote (using ssh) and a gdb debug connection is establised. For a run configuration, the binary is downloaded to the remote (using sftp) and then it is invoked (using ssh.)
The attached plugin (org.eclipse.rse.remotecdt) builds on top of CDT and integrates with RSE to introduce
a "Remote C/C++ Application" launch configuration. This launch configuration relies on the shell and file
services of the RSE connections.
The "Remote C/C++ Application" launch configuration enables the user to select from a list of RSE
connections which have the shell service available. For a debug launch, the binary is downloaded
to the remote using the file service of the RSE connection, gdbserver is automatically started
on the remote using the shell service and a gdb debug connection is establised. For a run launch,
the binary is downloaded to the remote using the file service and then it is invoked using the shell
service.
The extent of the current RSE integration with this launch configuration is the pulldown of connections. An important thing to note is that ssh and sftp are invoked directly by the plugin, without going through RSE. These are prime candidates for further integration. Also an RSE file service could be used in the future to select where the remote download should take place.
In the future, an RSE file service should be used in the future to select where the remote download
should take place. Right now the remote path is entered manually in a text field.
System Prerequisites
------------------
Local Host
A. ssh, sftp, gdb are on the path.
B. ssh and sftp are setup to connect to the desired remote without prompting.
A. It is necessary to create an RSE connection which has the file and shell services
available.
Remote Host
A. gdbserver is on the path.
@ -21,19 +30,28 @@ Remote Host
Installation of Necessary Eclipse Components
---------------------------------
1. Install Eclipse Platform M6.
2. Install CDT Runtime compatible with Eclipse M6.
From: http://cdt.eclipse.org/builds/3.1.0/index.html
I used the following build: I200604141755
1. Install Eclipse 3.2.
2. Install CDT 3.1.
3. Install RSE. This is the procedure I followed.
A. Checkout RSE from the repository using anonymous username.
B. Import the plugin directory and build all the RSE plugins.
4. Import the Remote CDT plugin attached to this bugzilla and build that as well.
4. Import the Remote CDT plugin and build that.
Using
----------
A "Remote C++ Application" launch configuration will be available. After building a binary which will run on your remote, try using this configuration to run or debug it.
A "Remote C++ Application" launch configuration will be available.
After building a binary which will run on your remote, try using this
configuration to run or debug it.
Current Limitations
-----------
There is no way to tell when the command on the remote has finished
running. As a result, the "Remote Shell" process will be left active
after the debug session dies and needs to be terminated manually. The
run process also needs to be terminated manually for the same reason.

View file

@ -1,14 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<?eclipse version="3.0"?>
<plugin>
<extension
point="org.eclipse.rse.core.systemTypes">
<systemType id="org.eclipse.rse.remotecdt.sshgdbserver"
name="Ssh/Gdbserver"
description="Place holder for host information used by the Remote C++ Application launch configuration."
icon="icons/full/obj16/systemlocal_obj.gif"
iconLive="icons/full/obj16/systemlocallive_obj.gif"/>
</extension>
<extension
point="org.eclipse.debug.core.launchConfigurationTypes">
<launchConfigurationType

View file

@ -19,7 +19,7 @@ import org.osgi.framework.BundleContext;
*/
public class Activator extends Plugin {
//The shared instance.
/* The shared instance */
private static Activator plugin;
/**

View file

@ -0,0 +1,98 @@
/*******************************************************************************
* Copyright (c) 2006 PalmSource, Inc.
* 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:
* Ewa Matejska (PalmSource)
*******************************************************************************/
package org.eclipse.rse.remotecdt;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import org.eclipse.rse.services.shells.IHostShell;
import org.eclipse.rse.services.shells.IHostShellChangeEvent;
import org.eclipse.rse.services.shells.IHostShellOutputListener;
public class HostShellAdapter extends Process implements
IHostShellOutputListener {
private IHostShell hostShell;
private PipedInputStream inputStream = null;
private PipedInputStream errorStream = null;
private HostShellOutputStream outputStream = null;
private PipedOutputStream hostShellInput = null;
private PipedOutputStream hostShellError = null;
public HostShellAdapter(IHostShell hostShell) throws java.io.IOException {
this.hostShell = hostShell;
hostShellInput = new PipedOutputStream();
hostShellError = new PipedOutputStream();
inputStream = new PipedInputStream(hostShellInput);
errorStream = new PipedInputStream(hostShellError);
outputStream = new HostShellOutputStream(hostShell);
this.hostShell.getStandardOutputReader().addOutputListener(this);
this.hostShell.getStandardErrorReader().addOutputListener(this);
}
public synchronized void destroy() {
hostShell.exit();
notifyAll();
try {
inputStream.close();
errorStream.close();
outputStream.close();
} catch (IOException e) {
// Ignore
}
}
public int exitValue() {
if(hostShell.isActive())
throw new IllegalThreadStateException();
// No way to tell what the exit value was.
return 0;
}
public InputStream getErrorStream() {
return errorStream;
}
public InputStream getInputStream() {
return inputStream;
}
public OutputStream getOutputStream() {
return outputStream;
}
public synchronized int waitFor() throws InterruptedException {
while(hostShell.isActive())
wait();
return 0;
}
public void shellOutputChanged(IHostShellChangeEvent event) {
Object[] input = event.getLines();
OutputStream outputStream = event.isError() ? hostShellError : hostShellInput;
try {
for(int i = 0; i < input.length; i++) {
outputStream.write(input[i].toString().getBytes());
outputStream.write('\n');
outputStream.flush();
}
} catch(IOException e) {
// Ignore
}
}
}

View file

@ -0,0 +1,43 @@
/*******************************************************************************
* Copyright (c) 2006 PalmSource, Inc.
* 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:
* Ewa Matejska (PalmSource)
*******************************************************************************/
package org.eclipse.rse.remotecdt;
import java.io.IOException;
import java.io.OutputStream;
import org.eclipse.rse.services.shells.IHostShell;
public class HostShellOutputStream extends OutputStream {
private IHostShell hostShell;
public HostShellOutputStream(IHostShell hostShell) {
this.hostShell = hostShell;
}
public void write(byte[] b) {
if(hostShell != null && b != null)
hostShell.writeToShell(new String(b));
}
public void write(byte[] b, int off, int len) {
if(hostShell != null && b != null)
hostShell.writeToShell(new String(b, off, len));
}
public void write(int b) throws IOException {
char[] array = { (char) b };
if(hostShell != null)
hostShell.writeToShell(new String(array));
}
}

View file

@ -12,19 +12,19 @@
package org.eclipse.rse.remotecdt;
import org.eclipse.cdt.debug.mi.core.IMILaunchConfigurationConstants;
import org.eclipse.cdt.debug.mi.core.IGDBServerMILaunchConfigurationConstants;
import org.eclipse.debug.core.DebugPlugin;
public interface IRemoteConnectionConfigurationConstants extends
IMILaunchConfigurationConstants {
IGDBServerMILaunchConfigurationConstants {
public static final String ATTR_REMOTE_CONNECTION =
DebugPlugin.getUniqueIdentifier() + ".REMOTE_TCP"; //$NON-NLS-1$
/*
* ATTR_TCP_PORT: gdbserver port.
*/
public static final String ATTR_TCP_PORT = "2345"; //$NON-NLS-1$
public static final String ATTR_DEFAULT_PORT = "2345"; //$NON-NLS-1$
public static final String ATTR_REMOTE_DEBUGGER_COMMAND = "gdbserver"; //$NON-NLS-1$
/*
* Generic Remote Path and Download options

View file

@ -0,0 +1,37 @@
package org.eclipse.rse.remotecdt;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.cdt.debug.core.CDebugCorePlugin;
import org.eclipse.cdt.debug.core.ICDebugConfiguration;
import org.eclipse.cdt.launch.ui.CDebuggerTab;
import org.eclipse.debug.core.ILaunchConfiguration;
public class RemoteCDebuggerTab extends CDebuggerTab {
static final private String GDBSERVER_DEBUGGER_NAME = "gdb/mi"; //$NON-NLS-1$
public RemoteCDebuggerTab(boolean attachMode) {
super(attachMode);
}
protected void loadDebuggerComboBox(ILaunchConfiguration config, String selection) {
ICDebugConfiguration[] debugConfigs = CDebugCorePlugin.getDefault().getDebugConfigurations();
String defaultSelection = selection;
List list = new ArrayList();
for(int i = 0; i < debugConfigs.length; i++) {
ICDebugConfiguration configuration = debugConfigs[i];
if(configuration.getName().equals(GDBSERVER_DEBUGGER_NAME)) {
list.add(configuration);
// Select as default selection
defaultSelection = configuration.getID();
break;
}
}
setInitializeDefault(defaultSelection.equals("") ? true : false); //$NON-NLS-1$
loadDebuggerCombo((ICDebugConfiguration[])list.toArray(
new ICDebugConfiguration[list.size()]), defaultSelection);
}
}

View file

@ -38,12 +38,11 @@ import org.eclipse.swt.widgets.Text;
public class RemoteCMainTab extends CMainTab {
private static final String[] SYSTEM_TYPE = {"Ssh/Gdbserver"}; //$NON-NLS-1$
/* Labels and Error Messages */
private static final String REMOTE_PROG_LABEL_TEXT = "Remote Path for C/C++ Application:";
private static final String SKIP_DOWNLOAD_BUTTON_TEXT = "Skip download to target path.";
private static final String REMOTE_PROG_TEXT_ERROR = "Remote executable path is not specified.";
private static final String CONNECTION_TEXT_ERROR = "Remote Connection must be selected.";
private static final String REMOTE_PROG_LABEL_TEXT = "Remote Path for C/C++ Application:"; //$NON-NLS-1$
private static final String SKIP_DOWNLOAD_BUTTON_TEXT = "Skip download to target path."; //$NON-NLS-1$
private static final String REMOTE_PROG_TEXT_ERROR = "Remote executable path is not specified."; //$NON-NLS-1$
private static final String CONNECTION_TEXT_ERROR = "Remote Connection must be selected."; //$NON-NLS-1$
/* Defaults */
private static final String REMOTE_PATH_DEFAULT = EMPTY_STRING;
@ -57,7 +56,6 @@ public class RemoteCMainTab extends CMainTab {
protected Button skipDownloadButton;
protected Button useLocalPathButton;
private boolean initialized = false;
SystemNewConnectionAction action = null;
public RemoteCMainTab(boolean terminalOption) {
@ -92,10 +90,7 @@ public class RemoteCMainTab extends CMainTab {
fProgText.addModifyListener(new ModifyListener() {
public void modifyText(ModifyEvent evt) {
if(initialized)
setLocalPathForRemotePath();
else
initialized = true;
setLocalPathForRemotePath();
}
});
@ -139,7 +134,7 @@ public class RemoteCMainTab extends CMainTab {
projComp.setLayoutData(gd);
connectionLabel = new Label(projComp, SWT.NONE);
connectionLabel.setText("Connection:");
connectionLabel.setText("Connection:"); //$NON-NLS-1$
gd = new GridData();
gd.horizontalSpan = 1;
connectionLabel.setLayoutData(gd);
@ -157,7 +152,7 @@ public class RemoteCMainTab extends CMainTab {
});
updateConnectionPulldown();
newRemoteConnectionButton = createPushButton(projComp, "New", null);
newRemoteConnectionButton = createPushButton(projComp, "New", null); //$NON-NLS-1$
newRemoteConnectionButton.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent evt) {
@ -244,7 +239,7 @@ public class RemoteCMainTab extends CMainTab {
remoteConnection = config.getAttribute(IRemoteConnectionConfigurationConstants.ATTR_REMOTE_CONNECTION,
""); //$NON-NLS-1$
} catch (CoreException ce) {
/* default to doing nothing */
// Ignore
}
String[] items = connectionCombo.getItems();
@ -269,24 +264,24 @@ public class RemoteCMainTab extends CMainTab {
{
action = new SystemNewConnectionAction(getControl().getShell(), false, false, null);
}
action.restrictSystemTypes(SYSTEM_TYPE);
try
{
action.run();
} catch (Exception exc)
} catch (Exception e)
{
/* Ignore for now */
// Ignore
}
}
protected void updateConnectionPulldown() {
connectionCombo.removeAll();
IHost[] connections = RSEUIPlugin.getTheSystemRegistry().getHostsBySystemType(SYSTEM_TYPE[0]);
IHost[] connections = RSEUIPlugin.getTheSystemRegistry().getHostsBySubSystemConfigurationCategory("shells"); //$NON-NLS-1$
for(int i = 0; i < connections.length; i++)
connectionCombo.add(connections[i].getAliasName());
if(connections.length > 0)
connectionCombo.select(0);
connectionCombo.select(connections.length - 1);
}
protected void updateTargetProgFromConfig(ILaunchConfiguration config) {
@ -294,8 +289,8 @@ public class RemoteCMainTab extends CMainTab {
try {
targetPath = config.getAttribute(IRemoteConnectionConfigurationConstants.ATTR_REMOTE_PATH,
REMOTE_PATH_DEFAULT);
} catch (CoreException ce) {
/* Ignore for now */
} catch (CoreException e) {
// Ignore
}
remoteProgText.setText(targetPath);
}
@ -306,7 +301,7 @@ public class RemoteCMainTab extends CMainTab {
downloadToTarget = config.getAttribute(IRemoteConnectionConfigurationConstants.ATTR_SKIP_DOWNLOAD_TO_TARGET,
SKIP_DOWNLOAD_TO_REMOTE_DEFAULT);
} catch (CoreException e) {
/* Ignore for now */
// Ignore for now
}
skipDownloadButton.setSelection(downloadToTarget);
}
@ -317,12 +312,13 @@ public class RemoteCMainTab extends CMainTab {
* local executable path.
*/
private void setLocalPathForRemotePath() {
String name = fProgText.getText().trim();
if (name.length() != 0) {
String programName = fProgText.getText().trim();
String remoteName = remoteProgText.getText().trim();
if (programName.length() != 0 && remoteName.length() == 0) {
IProject project = getCProject().getProject();
IPath exePath = new Path(name);
IPath exePath = new Path(programName);
if (!exePath.isAbsolute()) {
exePath = project.getFile(name).getLocation();
exePath = project.getFile(programName).getLocation();
}
String path = exePath.toString();
remoteProgText.setText(path);

View file

@ -28,6 +28,7 @@ public class RemoteLaunchConfigurationTabGroup extends
ILaunchConfigurationTab[] tabs = new ILaunchConfigurationTab[] {
new RemoteCMainTab(true),
new CArgumentsTab(),
new RemoteCDebuggerTab(false),
new SourceLookupTab(),
new CommonTab()
};

View file

@ -10,27 +10,27 @@
*******************************************************************************/
package org.eclipse.rse.remotecdt;
import java.io.IOException;
import java.io.OutputStream;
import java.io.File;
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.ICDIDebugger2;
import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants;
import org.eclipse.cdt.debug.core.cdi.CDIException;
import org.eclipse.cdt.debug.core.cdi.ICDISession;
import org.eclipse.cdt.debug.core.cdi.model.ICDITarget;
import org.eclipse.cdt.debug.mi.core.GDBServerCDIDebugger;
import org.eclipse.cdt.debug.mi.core.GDBServerCDIDebugger2;
import org.eclipse.cdt.debug.mi.core.IGDBServerMILaunchConfigurationConstants;
import org.eclipse.cdt.launch.AbstractCLaunchDelegate;
import org.eclipse.cdt.utils.spawner.Spawner;
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;
@ -40,16 +40,21 @@ import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
import org.eclipse.debug.core.ILaunchManager;
import org.eclipse.debug.core.model.IProcess;
import org.eclipse.rse.core.model.IHost;
import org.eclipse.rse.core.subsystems.ISubSystem;
import org.eclipse.rse.services.IService;
import org.eclipse.rse.services.files.IFileService;
import org.eclipse.rse.services.shells.IHostShell;
import org.eclipse.rse.services.shells.IShellService;
import org.eclipse.rse.subsystems.files.core.servicesubsystem.IFileServiceSubSystem;
import org.eclipse.rse.subsystems.shells.core.subsystems.servicesubsystem.IShellServiceSubSystem;
import org.eclipse.rse.ui.RSEUIPlugin;
import org.eclipse.swt.widgets.Display;
public class RemoteRunLaunchDelegate extends AbstractCLaunchDelegate {
private final static String REMOTE_GDBSERVER_COMMAND = "gdbserver"; //$NON-NLS-1$
private final static String SFTP_COMMAND = "sftp"; //$NON-NLS-1$
private final static String SFTP_COMMAND_ARGS = "-b -"; //$NON-NLS-1$
private final static String SSH_COMMAND = "ssh"; //$NON-NLS-1$
private final static String SYSTEM_TYPE = "Ssh/Gdbserver"; //$NON-NLS-1$
private final static String SHELL_SERVICE = "shell.service"; //$NON-NLS-1$
private final static String FILE_SERVICE = "file.service"; //$NON-NLS-1$
/*
* (non-Javadoc)
* @see org.eclipse.debug.core.model.ILaunchConfigurationDelegate#launch
@ -65,49 +70,51 @@ public class RemoteRunLaunchDelegate extends AbstractCLaunchDelegate {
}
String arguments = getProgramArguments(config);
String remoteExePath = config.getAttribute(IRemoteConnectionConfigurationConstants.ATTR_REMOTE_PATH,
""); //$NON-NLS-1$
String remoteExePath = config.getAttribute(IRemoteConnectionConfigurationConstants.ATTR_REMOTE_PATH, ""); //$NON-NLS-1$
if(mode.equals(ILaunchManager.DEBUG_MODE)){
setDefaultSourceLocator(launch, config);
ICDISession dsession = null;
String debugMode = config.getAttribute(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_START_MODE,
ICDTLaunchConfigurationConstants.DEBUGGER_MODE_RUN);
if (debugMode.equals(ICDTLaunchConfigurationConstants.DEBUGGER_MODE_RUN)) {
/* Download the binary to the remote before debugging using the scp program*/
remoteSftpDownload(config, launch, exePath.toString(), remoteExePath);
/* Default to using the GDBServerCDIDebugger. */
GDBServerCDIDebugger debugger = new GDBServerCDIDebugger();
/* Automatically start up the gdbserver to be used by the GDBServerCDIDebugger on the remote
* using ssh.
*/
String command_arguments = ":" + IRemoteConnectionConfigurationConstants.ATTR_TCP_PORT + " " + remoteExePath; //$NON-NLS-1$ //$NON-NLS-2$
if(arguments != null && !arguments.equals("")) //$NON-NLS-1$
command_arguments += " " + arguments; //$NON-NLS-1$
Process sshProcess = remoteSshExec(config, REMOTE_GDBSERVER_COMMAND, command_arguments);
DebugPlugin.newProcess(launch, sshProcess, renderProcessLabel(REMOTE_GDBSERVER_COMMAND));
/* Pre-set configuration constants for the GDBSERVERCDIDebugger to indicate how the gdbserver
* was automatically started on the remote. GDBServerCDIDebugger uses these to figure out how
* to connect to the remote gdbserver.
*/
ILaunchConfigurationWorkingCopy wc = config.getWorkingCopy();
wc.setAttribute(IGDBServerMILaunchConfigurationConstants.ATTR_REMOTE_TCP, true);
wc.setAttribute(IGDBServerMILaunchConfigurationConstants.ATTR_HOST, getRemoteHostname(config));
wc.setAttribute(IGDBServerMILaunchConfigurationConstants.ATTR_PORT,
IRemoteConnectionConfigurationConstants.ATTR_TCP_PORT);
dsession = debugger.createLaunchSession(wc.doSave(), exeFile, new SubProgressMonitor(monitor, 8));
Process remoteShellProcess = null;
ICDISession dsession = null;
try {
/* Assume that stopInMain is true until the Debugger tab is added */
boolean stopInMain = true;
// Download the binary to the remote before debugging.
remoteFileDownload(config, launch, exePath.toString(), remoteExePath);
// Automatically start up the gdbserver. In the future this should be expanded to launch
// an arbitrary remote damon.
String command_arguments = ":" + IRemoteConnectionConfigurationConstants.ATTR_DEFAULT_PORT + " " //$NON-NLS-1$ //$NON-NLS-2$
+ spaceEscapify(remoteExePath);
if(arguments != null && !arguments.equals("")) //$NON-NLS-1$
command_arguments += " " + arguments; //$NON-NLS-1$
remoteShellProcess = remoteShellExec(config, IRemoteConnectionConfigurationConstants.ATTR_REMOTE_DEBUGGER_COMMAND,
command_arguments);
DebugPlugin.newProcess(launch, remoteShellProcess, "Remote Shell"); //$NON-NLS-1$
// Pre-set configuration constants for the GDBSERVERCDIDebugger to indicate how the gdbserver
// was automatically started on the remote. GDBServerCDIDebugger uses these to figure out how
// to connect to the remote gdbserver.
ILaunchConfigurationWorkingCopy wc = config.getWorkingCopy();
wc.setAttribute(IGDBServerMILaunchConfigurationConstants.ATTR_REMOTE_TCP, true);
wc.setAttribute(IGDBServerMILaunchConfigurationConstants.ATTR_HOST, getRemoteHostname(config));
wc.setAttribute(IGDBServerMILaunchConfigurationConstants.ATTR_PORT,
IRemoteConnectionConfigurationConstants.ATTR_DEFAULT_PORT);
wc.doSave();
// Default to using the GDBServerCDIDebugger.
GDBServerCDIDebugger2 debugger = new GDBServerCDIDebugger2();
dsession = ((ICDIDebugger2)debugger).createSession(launch, exePath.toFile(),
new SubProgressMonitor(monitor, 8));
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 );
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++) {
@ -118,12 +125,15 @@ public class RemoteRunLaunchDelegate extends AbstractCLaunchDelegate {
renderProcessLabel(exePath.toOSString()), getDefaultProcessMap());
}
CDIDebugModel.newDebugTarget(launch, project.getProject(), targets[i],
renderProcessLabel("gdbserver debugger"),
renderProcessLabel("gdbserver debugger"), //$NON-NLS-1$
iprocess, exeFile, true, false, stopSymbol, true);
}
} catch (CoreException e) {
try {
dsession.terminate();
if(dsession != null)
dsession.terminate();
if(remoteShellProcess != null)
remoteShellProcess.destroy();
} catch (CDIException e1) {
// ignore
}
@ -132,26 +142,26 @@ public class RemoteRunLaunchDelegate extends AbstractCLaunchDelegate {
}
} else if(mode.equals(ILaunchManager.RUN_MODE)) {
/* Download the binary to the remote before debugging */
remoteSftpDownload(config, launch, exePath.toString(),remoteExePath );
/* Use ssh to launch the binary on the remote */
Process process = remoteSshExec(config, remoteExePath, arguments);
DebugPlugin.newProcess(launch, process, renderProcessLabel(exePath.toOSString()));
Process remoteProcess = null;
try {
// Download the binary to the remote before debugging.
remoteFileDownload(config, launch, exePath.toString(),remoteExePath );
// Use a remote shell to launch the binary.
remoteProcess = remoteShellExec(config, remoteExePath, arguments);
DebugPlugin.newProcess(launch, remoteProcess, renderProcessLabel(exePath.toOSString()));
} catch (CoreException e) {
if(remoteProcess != null)
remoteProcess.destroy();
throw e;
}
} else {
IStatus status = new Status(IStatus.ERROR, getPluginID(),
IStatus.OK, "Unidentified mode " + mode + " passed to RemoteRunLaunchDelegate", null);
IStatus.OK, "Unidentified mode " + mode + " passed to RemoteRunLaunchDelegate", null); //$NON-NLS-1$ //$NON-NLS-2$
throw new CoreException(status);
}
}
private String quotify(String inputString) {
if(inputString == null)
return null;
return '"' + inputString + '"';
}
private String spaceEscapify(String inputString) {
if(inputString == null)
return null;
@ -159,109 +169,116 @@ public class RemoteRunLaunchDelegate extends AbstractCLaunchDelegate {
return inputString.replaceAll(" ", "\\\\ "); //$NON-NLS-1$ //$NON-NLS-2$
}
protected Process remoteSftpDownload(ILaunchConfiguration config, ILaunch launch, String localExePath, String remoteExePath)
throws CoreException {
boolean skipDownload = config.getAttribute(IRemoteConnectionConfigurationConstants.ATTR_SKIP_DOWNLOAD_TO_TARGET,
false);
if(skipDownload)
//Nothing to do. Download is skipped.
return null;
protected IHost getCurrentConnection(ILaunchConfiguration config) throws CoreException {
String remoteConnection = config.getAttribute(IRemoteConnectionConfigurationConstants.ATTR_REMOTE_CONNECTION, ""); //$NON-NLS-1$
String arguments = SFTP_COMMAND_ARGS + " " + getRemoteHostname(config); //$NON-NLS-1$
Process p = null;
try {
p = execLocal(SFTP_COMMAND, arguments);
DebugPlugin.newProcess(launch, p, renderProcessLabel(SFTP_COMMAND));
OutputStream outStream = p.getOutputStream();
String putCommand = "put " + quotify(localExePath) + " " + quotify(remoteExePath) + "\n"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
String exitCommand = "exit\n"; //$NON-NLS-1$
// Execute the put and then the exit command.
outStream.write(putCommand.getBytes());
outStream.write(exitCommand.getBytes());
if(p.waitFor() != 0) {
IStatus status = new Status(IStatus.ERROR, getPluginID(),
IStatus.OK, "Couldn't download program to remote. See console for reason.", null);
throw new CoreException(status);
}
} catch (InterruptedException e) {
} catch (IOException e) {
}
return null;
}
protected String getRemoteHostname(ILaunchConfiguration config) throws CoreException{
String remoteConnection = config.getAttribute(IRemoteConnectionConfigurationConstants.ATTR_REMOTE_CONNECTION,
""); //$NON-NLS-1$
IHost[] connections = RSEUIPlugin.getTheSystemRegistry().getHostsBySystemType(SYSTEM_TYPE);
IHost[] connections = RSEUIPlugin.getTheSystemRegistry().getHosts();
int i = 0;
for(i = 0; i < connections.length; i++)
if(connections[i].getAliasName().equals(remoteConnection))
break;
if(i >= connections.length) {
IStatus status = new Status(IStatus.ERROR, getPluginID(),
IStatus.OK, "Internal Error: Could not find the remote connection.\n", null);
throw new CoreException(status);
abort("Could not find the remote connection.", null, ICDTLaunchConfigurationConstants.ERR_INTERNAL_ERROR); //$NON-NLS-1$
}
return connections[i].getHostName();
return connections[i];
}
protected Process remoteSshExec(ILaunchConfiguration config, String remoteCommandPath,
protected IService getConnectedRemoteService(ILaunchConfiguration config, String kindOfService)
throws CoreException {
// Check that the service requested is file or shell.
if(!kindOfService.equals(SHELL_SERVICE) && !kindOfService.equals(FILE_SERVICE))
abort("Wrong service requested.", null, ICDTLaunchConfigurationConstants.ERR_INTERNAL_ERROR); //$NON-NLS-1$
IHost currentConnection = getCurrentConnection(config);
ISubSystem[] subSystems = currentConnection.getSubSystems();
int i = 0;
for(i = 0; i < subSystems.length; i++) {
if(subSystems[i] instanceof IShellServiceSubSystem && kindOfService.equals(SHELL_SERVICE))
break;
if(subSystems[i] instanceof IFileServiceSubSystem && kindOfService.equals(FILE_SERVICE))
break;
}
if(i >= subSystems.length)
abort("No subsystem found.\n", null, ICDTLaunchConfigurationConstants.ERR_INTERNAL_ERROR); //$NON-NLS-1$
// Need to run this in the UI thread
final ISubSystem subsystem = subSystems[i];
Display.getDefault().syncExec(new Runnable()
{
public void run()
{ try {
subsystem.connect(false);
} catch (Exception e) {
// Ignore
}
}
});
if(!subsystem.isConnected())
abort("Could not connection to the remote system.", null, ICDTLaunchConfigurationConstants.ERR_INTERNAL_ERROR); //$NON-NLS-1$
if(kindOfService.equals(SHELL_SERVICE))
return ((IShellServiceSubSystem) subsystem).getShellService();
else
return ((IFileServiceSubSystem) subsystem).getFileService();
}
protected Process remoteFileDownload(ILaunchConfiguration config, ILaunch launch,
String localExePath, String remoteExePath) throws CoreException {
boolean skipDownload = config.getAttribute(IRemoteConnectionConfigurationConstants.ATTR_SKIP_DOWNLOAD_TO_TARGET, false);
if(skipDownload)
// Nothing to do. Download is skipped.
return null;
IFileService fileService = (IFileService) getConnectedRemoteService(config, FILE_SERVICE);
File file = new File(localExePath);
Path remotePath = new Path(remoteExePath);
try {
fileService.upload(new NullProgressMonitor(), file, remotePath.removeLastSegments(1).toString(),
remotePath.lastSegment(), true, null, null);
// Need to change the permissions to match the original file permissions because of a bug in upload
Process p = remoteShellExec(config, "chmod", "+x " + spaceEscapify(remotePath.toString())); //$NON-NLS-1$ //$NON-NLS-2$
Thread.sleep(500);
p.destroy();
} catch (Exception e) {
abort("Error during file upload.", e, ICDTLaunchConfigurationConstants.ERR_INTERNAL_ERROR ); //$NON-NLS-1$
}
return null;
}
protected String getRemoteHostname(ILaunchConfiguration config) throws CoreException {
IHost currentConnection = getCurrentConnection(config);
return currentConnection.getHostName();
}
protected Process remoteShellExec(ILaunchConfiguration config, String remoteCommandPath,
String arguments) throws CoreException {
String remote_command = arguments == null ? spaceEscapify(remoteCommandPath) :
spaceEscapify(remoteCommandPath) + " " + arguments; //$NON-NLS-1$
String ssh_arguments = getRemoteHostname(config) + " " + remote_command; //$NON-NLS-1$
IShellService shellService = (IShellService) getConnectedRemoteService(config, SHELL_SERVICE);
Process p = execLocal(SSH_COMMAND, ssh_arguments);
return p;
// This is necessary because runCommand does not actually run the command right now.
IHostShell hostShell = shellService.launchShell(new NullProgressMonitor(), "",null); //$NON-NLS-1$
hostShell.writeToShell(remote_command);
/* In the future the Shell subsystem from RSE should be used instead of invoking ssh and scp
* directly. The code to accomplish this might look like below.
*/
/*ISubSystem[] subSystems = connections[i].getSubSystems();
for(i = 0; i < subSystems.length; i++)
if(subSystems[i] instanceof IShellServiceSubSystem)
break;
if(i >= subSystems.length)
abort("Internal Error: No shell subsystem found.\n", null, 0);
IShellServiceSubSystem shellSubSystem = (IShellServiceSubSystem) subSystems[i];
IShellService shellService = shellSubSystem.getShellService();
String command = arguments == null ? file.toOSString() : file.toOSString() + " " + arguments;
IHostShell hostShell = shellService.runCommand(null, file.removeLastSegments(1).toOSString(),command, null);
Process p = null;
try {
p = new HostShellProcess(hostShell);
p = new HostShellAdapter(hostShell);
} catch (Exception e) {
if (p != null) {
p.destroy();
}
abort("Internal Error: Could not create the hostShellProcess.\n", null, 0);
abort("Could not create the hostShellProcess.\n", null, ICDTLaunchConfigurationConstants.ERR_INTERNAL_ERROR); //$NON-NLS-1$
}
*/
return p;
}
protected Process execLocal(String file, String arguments) throws CoreException {
Process p = null;
String command = file + " " + arguments; //$NON-NLS-1$
try {
p = new Spawner(command, false);
} catch (Exception e) {
if (p != null) {
p.destroy();
}
IStatus status = new Status(IStatus.ERROR, getPluginID(),
IStatus.OK, "Internal Error: Could not execute command.\n", null);
throw new CoreException(status);
}
return p;
}
protected String getPluginID() {
return "org.eclipse.rse.remotecdt"; //$NON-NLS-1$
}