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

Bug 303808: Support for full GDB console

This patch provides a full GDB console in the standard Console view.
The full console is disabled for Windows due to PTY limitations.

The new console will be triggered automatically when using GDB 7.12 (or
its pre-release) and running on Linux.

Known issues with this patch:
- Cloning the console view breaks the console
- Pinning the console view is not supported
- Interrupting execution in all-stop mode fails with the new console
- Pagination causes some events to be lost (all-stop only)

Change-Id: Iee6ef5228ca17bd829eb743cb41a142afe6714dc
This commit is contained in:
Marc Khouzam 2016-03-06 13:52:27 -05:00 committed by Gerrit Code Review @ Eclipse.org
parent 91c342b77c
commit 0caa750ef0
28 changed files with 1072 additions and 77 deletions

View file

@ -347,6 +347,9 @@ Java and all Java-based trademarks are trademarks of Oracle Corporation in the U
<plugin id="org.eclipse.swt.win32.win32.x86_64" fragment="true"/>
<plugin id="org.eclipse.team.core"/>
<plugin id="org.eclipse.team.ui"/>
<plugin id="org.eclipse.tm.terminal.control"/>
<plugin id="org.eclipse.tm.terminal.view.core"/>
<plugin id="org.eclipse.tm.terminal.view.ui"/>
<plugin id="org.eclipse.text"/>
<plugin id="org.eclipse.tools.templates.core"/>
<plugin id="org.eclipse.tools.templates.ui"/>

View file

@ -25,7 +25,10 @@ Require-Bundle: org.eclipse.ui,
org.eclipse.core.filesystem;bundle-version="1.2.0",
org.eclipse.cdt.launch;bundle-version="6.1.0",
org.eclipse.debug.core,
org.eclipse.core.resources
org.eclipse.core.resources,
org.eclipse.tm.terminal.control;bundle-version="4.0.0",
org.eclipse.tm.terminal.view.core;bundle-version="4.0.0",
org.eclipse.tm.terminal.view.ui;bundle-version="4.1.0"
Bundle-ActivationPolicy: lazy
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Export-Package: org.eclipse.cdt.dsf.gdb.internal.ui;x-friends:="org.eclipse.cdt.docker.launcher",

View file

@ -515,9 +515,10 @@
class="org.eclipse.cdt.dsf.gdb.internal.ui.console.ConsolePageParticipant"
id="org.eclipse.cdt.dsf.gdb.ui.dsfGdbConsolePageParticipant">
<enablement>
<instanceof
value="org.eclipse.ui.console.IOConsole">
</instanceof>
<or>
<instanceof value="org.eclipse.ui.console.IOConsole"/>
<instanceof value="org.eclipse.cdt.dsf.gdb.internal.ui.console.GdbCliConsole"/>
</or>
</enablement>
</consolePageParticipant>
</extension>

View file

@ -12,6 +12,7 @@
package org.eclipse.cdt.dsf.gdb.internal.ui;
import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin;
import org.eclipse.cdt.dsf.gdb.internal.ui.console.GdbCliConsoleManager;
import org.eclipse.cdt.dsf.gdb.internal.ui.console.TracingConsoleManager;
import org.eclipse.cdt.dsf.gdb.launching.GdbLaunch;
import org.eclipse.cdt.dsf.gdb.launching.LaunchMessages;
@ -46,6 +47,7 @@ public class GdbUIPlugin extends AbstractUIPlugin {
private static BundleContext fgBundleContext;
private static TracingConsoleManager fTracingConsoleManager;
private static GdbCliConsoleManager fGdbConsoleManager;
private static IPreferenceStore fCorePreferenceStore;
@ -67,6 +69,9 @@ public class GdbUIPlugin extends AbstractUIPlugin {
fTracingConsoleManager = new TracingConsoleManager();
fTracingConsoleManager.startup();
fGdbConsoleManager = new GdbCliConsoleManager();
fGdbConsoleManager.startup();
}
/*
@ -76,6 +81,7 @@ public class GdbUIPlugin extends AbstractUIPlugin {
@Override
public void stop(BundleContext context) throws Exception {
fTracingConsoleManager.shutdown();
fGdbConsoleManager.shutdown();
disposeAdapterSets();
plugin = null;

View file

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2010 Marc-Andre Laperle and others.
* Copyright (c) 2010, 2016 Marc-Andre Laperle 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
@ -17,7 +17,7 @@ import org.eclipse.osgi.util.NLS;
*/
public class ConsoleMessages extends NLS {
public static String ConsoleMessages_trace_console_name;
public static String ConsoleMessages_trace_console_terminated;
public static String ConsoleMessages_console_terminated;
public static String ConsoleMessages_save_action_tooltip;
public static String ConsoleMessages_save_confirm_overwrite_title;

View file

@ -1,5 +1,5 @@
##########################################################################
# Copyright (c) 2010 Marc-Andre Laperle and others.
# Copyright (c) 2010, 2016 Marc-Andre Laperle 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
@ -10,7 +10,7 @@
##########################################################################
ConsoleMessages_trace_console_name=gdb traces
ConsoleMessages_trace_console_terminated=<terminated>
ConsoleMessages_console_terminated=<terminated>
ConsoleMessages_save_action_tooltip=Save console content
ConsoleMessages_save_confirm_overwrite_title=Confirm overwrite

View file

@ -57,7 +57,8 @@ public class ConsolePageParticipant implements IConsolePageParticipant, IDebugCo
DebugUITools.getDebugContextManager().getContextService(fPage.getSite().getWorkbenchWindow()).addDebugContextListener(this);
}
if(console instanceof TracingConsole || isConsoleGdbCli(console)) {
if (console instanceof TracingConsole ||
(isConsoleGdbCli(console) && console instanceof TextConsole)) {
TextConsole textConsole = (TextConsole) console;
// Add the save console action
@ -81,6 +82,9 @@ public class ConsolePageParticipant implements IConsolePageParticipant, IDebugCo
org.eclipse.debug.ui.console.IConsole debugConsole = (org.eclipse.debug.ui.console.IConsole)console;
return (debugConsole.getProcess() instanceof GDBProcess);
}
if (console instanceof GdbCliConsole) {
return true;
}
return false;
}
@ -152,6 +156,10 @@ public class ConsolePageParticipant implements IConsolePageParticipant, IDebugCo
return null;
}
if (context instanceof GDBProcess) {
return (GDBProcess)context;
}
if (context != null) {
// Look for the process that this context refers to, so we can select its console
IDMContext dmc = context.getAdapter(IDMContext.class);
@ -187,12 +195,18 @@ public class ConsolePageParticipant implements IConsolePageParticipant, IDebugCo
return null;
}
/* (non-Javadoc)
* @see org.eclipse.debug.internal.ui.contexts.provisional.IDebugContextListener#contextEvent(org.eclipse.debug.internal.ui.contexts.provisional.DebugContextEvent)
*/
@Override
public void debugContextChanged(DebugContextEvent event) {
if ((event.getFlags() & DebugContextEvent.ACTIVATED) > 0) {
if (fView != null && fConsole instanceof GdbCliConsole) {
IProcess currentProcess = getCurrentProcess();
if (currentProcess instanceof GDBProcess &&
((GdbCliConsole)fConsole).getLaunch().equals(currentProcess.getLaunch())) {
fView.display(fConsole);
}
return;
}
IProcess consoleProcess = getConsoleProcess();
if (fView != null && consoleProcess != null && consoleProcess.equals(getCurrentProcess())) {
fView.display(fConsole);

View file

@ -0,0 +1,80 @@
/*******************************************************************************
* Copyright (c) 2015 Ericsson 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
*******************************************************************************/
package org.eclipse.cdt.dsf.gdb.internal.ui.console;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.ui.DebugUITools;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.console.AbstractConsole;
import org.eclipse.ui.console.IConsoleView;
import org.eclipse.ui.part.IPageBookViewPage;
/**
* A GDB CLI console.
* This console actually runs a GDB process in CLI mode to achieve a
* full-featured CLI interface. This is only supported with GDB >= 7.12
* and if IGDBBackend.isFullGdbConsoleSupported() returns true.
*/
public class GdbCliConsole extends AbstractConsole {
private final ILaunch fLaunch;
private String fLabel;
public GdbCliConsole(ILaunch launch, String label) {
super(label, null);
fLaunch = launch;
fLabel = label;
resetName();
}
public ILaunch getLaunch() { return fLaunch; }
public void resetName() {
String newName = computeName();
String name = getName();
if (!name.equals(newName)) {
PlatformUI.getWorkbench().getDisplay().asyncExec(() -> setName(newName));
}
}
protected String computeName() {
String label = fLabel;
ILaunchConfiguration config = fLaunch.getLaunchConfiguration();
if (config != null && !DebugUITools.isPrivate(config)) {
String type = null;
try {
type = config.getType().getName();
} catch (CoreException e) {
}
StringBuffer buffer = new StringBuffer();
buffer.append(config.getName());
if (type != null) {
buffer.append(" ["); //$NON-NLS-1$
buffer.append(type);
buffer.append("] "); //$NON-NLS-1$
}
buffer.append(label);
label = buffer.toString();
}
if (fLaunch.isTerminated()) {
return ConsoleMessages.ConsoleMessages_console_terminated + label;
}
return label;
}
@Override
public IPageBookViewPage createPage(IConsoleView view) {
view.setFocus();
return new GdbCliConsolePage(this);
}
}

View file

@ -0,0 +1,229 @@
/*******************************************************************************
* Copyright (c) 2009, 2015 Ericsson 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:
* Ericsson - initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.dsf.gdb.internal.ui.console;
import java.util.concurrent.RejectedExecutionException;
import org.eclipse.cdt.dsf.concurrent.ConfinedToDsfExecutor;
import org.eclipse.cdt.dsf.concurrent.DsfRunnable;
import org.eclipse.cdt.dsf.gdb.internal.ui.GdbUIPlugin;
import org.eclipse.cdt.dsf.gdb.launching.GdbLaunch;
import org.eclipse.cdt.dsf.gdb.service.IGDBBackend;
import org.eclipse.cdt.dsf.mi.service.IMIBackend;
import org.eclipse.cdt.dsf.mi.service.IMIBackend.BackendStateChangedEvent;
import org.eclipse.cdt.dsf.service.DsfServiceEventHandler;
import org.eclipse.cdt.dsf.service.DsfServicesTracker;
import org.eclipse.cdt.dsf.service.DsfSession;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.ILaunchesListener2;
import org.eclipse.ui.console.ConsolePlugin;
import org.eclipse.ui.console.IConsole;
import org.eclipse.ui.console.IConsoleManager;
/**
* A console manager for GDB sessions which adds and removes
* gdb cli consoles.
*
* There is a single such console per debug session.
* This console interacts directly with the GDB process using
* the standard GDB CLI interface.
* These consoles cannot be enabled/disabled by the user.
* However, they are only supported by GDB >= 7.12;
* to handle this limitation, the console manager will use the DSF Backend
* service to establish if it should start a gdb cli console or not.
*/
public class GdbCliConsoleManager implements ILaunchesListener2 {
/**
* Start the tracing console. We don't do this in a constructor, because
* we need to use <code>this</code>.
*/
public void startup() {
// Listen to launch events
DebugPlugin.getDefault().getLaunchManager().addLaunchListener(this);
}
public void shutdown() {
DebugPlugin.getDefault().getLaunchManager().removeLaunchListener(this);
removeAllCliConsoles();
}
protected void removeAllCliConsoles() {
ILaunch[] launches = DebugPlugin.getDefault().getLaunchManager().getLaunches();
for (ILaunch launch : launches) {
removeCliConsole(launch);
}
}
@Override
public void launchesAdded(ILaunch[] launches) {
for (ILaunch launch : launches) {
addCliConsole(launch);
}
}
@Override
public void launchesChanged(ILaunch[] launches) {
}
@Override
public void launchesRemoved(ILaunch[] launches) {
for (ILaunch launch : launches) {
removeCliConsole(launch);
}
}
@Override
public void launchesTerminated(ILaunch[] launches) {
for (ILaunch launch : launches) {
renameCliConsole(launch);
}
}
protected void addCliConsole(ILaunch launch) {
// Full CLI GDB consoles are only added for GdbLaunches
if (launch instanceof GdbLaunch) {
new GdbCliConsoleCreator((GdbLaunch)launch).init();
}
}
protected void removeCliConsole(ILaunch launch) {
GdbCliConsole console = getCliConsole(launch);
if (console != null) {
ConsolePlugin.getDefault().getConsoleManager().removeConsoles(new IConsole[]{console});
}
}
protected void renameCliConsole(ILaunch launch) {
GdbCliConsole console = getCliConsole(launch);
if (console != null) {
console.resetName();
}
}
private GdbCliConsole getCliConsole(ILaunch launch) {
ConsolePlugin plugin = ConsolePlugin.getDefault();
if (plugin != null) {
// This plugin can be null when running headless JUnit tests
IConsoleManager manager = plugin.getConsoleManager();
IConsole[] consoles = manager.getConsoles();
for (IConsole console : consoles) {
if (console instanceof GdbCliConsole) {
GdbCliConsole gdbConsole = (GdbCliConsole)console;
if (gdbConsole.getLaunch().equals(launch)) {
return gdbConsole;
}
}
}
}
return null;
}
/**
* Class that determines if a GdbCliConsole should be created for
* this particular Gdblaunch. It figures this out by asking the
* Backend service.
*/
private class GdbCliConsoleCreator {
private GdbLaunch fLaunch;
private DsfSession fSession;
public GdbCliConsoleCreator(GdbLaunch launch) {
fLaunch = launch;
fSession = launch.getSession();
}
public void init() {
try {
fSession.getExecutor().submit(new DsfRunnable() {
@Override
public void run() {
// Look for backend service right away. It probably
// won't be available yet but we must make sure.
DsfServicesTracker tracker = new DsfServicesTracker(GdbUIPlugin.getBundleContext(), fSession.getId());
IGDBBackend backend = tracker.getService(IGDBBackend.class);
tracker.dispose();
if (backend != null) {
// Backend service already available, us it!
verifyAndCreateCliConsole(backend);
} else {
// Backend service not available yet, let's wait for it to start.
fSession.addServiceEventListener(new GdbBackendStartedListener(GdbCliConsoleCreator.this, fSession), null);
}
}
});
} catch (RejectedExecutionException e) {
}
}
@ConfinedToDsfExecutor("fSession.getExecutor()")
private void verifyAndCreateCliConsole(IGDBBackend backend) {
if (backend != null && backend.isFullGdbConsoleSupported()) {
// Create an new Cli console .
String gdbVersion;
try {
gdbVersion = fLaunch.getGDBVersion();
} catch (CoreException e) {
assert false : "Should not happen since the gdb version is cached"; //$NON-NLS-1$
gdbVersion = "???"; //$NON-NLS-1$
}
String consoleTitle = fLaunch.getGDBPath().toOSString().trim() + " (" + gdbVersion +")"; //$NON-NLS-1$ //$NON-NLS-2$
GdbCliConsole console = new GdbCliConsole(fLaunch, consoleTitle);
// Register this console
ConsolePlugin.getDefault().getConsoleManager().addConsoles(new IConsole[]{console});
// Very important to make sure the console view is open or else things will not work
ConsolePlugin.getDefault().getConsoleManager().showConsoleView(console);
}
// Else, not the right type of backend service, or the service said not to start a GdbCliConsole
}
@ConfinedToDsfExecutor("fSession.getExecutor()")
private void backendStarted() {
DsfServicesTracker tracker = new DsfServicesTracker(GdbUIPlugin.getBundleContext(), fSession.getId());
IGDBBackend backend = tracker.getService(IGDBBackend.class);
tracker.dispose();
verifyAndCreateCliConsole(backend);
}
}
/**
* Class used to listen for Backend started event which indicate
* the DSF-GDB backend service can be used.
* This class must be public to receive the event.
*/
public class GdbBackendStartedListener {
private DsfSession fSession;
private GdbCliConsoleCreator fCreator;
public GdbBackendStartedListener(GdbCliConsoleCreator creator, DsfSession session) {
fCreator = creator;
fSession = session;
}
@DsfServiceEventHandler
public void eventDispatched(BackendStateChangedEvent event) {
if (event.getState() == IMIBackend.State.STARTED &&
event.getSessionId().equals(fSession.getId()))
{
fCreator.backendStarted();
// No longer need to receive events.
fSession.removeServiceEventListener(this);
}
}
}
}

View file

@ -0,0 +1,163 @@
/*******************************************************************************
* Copyright (c) 2015, 2016 Ericsson 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
*******************************************************************************/
package org.eclipse.cdt.dsf.gdb.internal.ui.console;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.RejectedExecutionException;
import org.eclipse.cdt.dsf.concurrent.DsfRunnable;
import org.eclipse.cdt.dsf.gdb.internal.ui.GdbUIPlugin;
import org.eclipse.cdt.dsf.gdb.launching.GdbLaunch;
import org.eclipse.cdt.dsf.gdb.service.IGDBBackend;
import org.eclipse.cdt.dsf.service.DsfServicesTracker;
import org.eclipse.cdt.dsf.service.DsfSession;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.tm.internal.terminal.control.ITerminalListener;
import org.eclipse.tm.internal.terminal.control.ITerminalViewControl;
import org.eclipse.tm.internal.terminal.control.TerminalViewControlFactory;
import org.eclipse.tm.internal.terminal.provisional.api.ITerminalConnector;
import org.eclipse.tm.internal.terminal.provisional.api.ITerminalControl;
import org.eclipse.tm.internal.terminal.provisional.api.TerminalState;
import org.eclipse.tm.terminal.view.core.interfaces.ITerminalServiceOutputStreamMonitorListener;
import org.eclipse.tm.terminal.view.core.interfaces.constants.ITerminalsConnectorConstants;
import org.eclipse.tm.terminal.view.ui.interfaces.ILauncherDelegate;
import org.eclipse.tm.terminal.view.ui.launcher.LauncherDelegateManager;
import org.eclipse.ui.part.Page;
public class GdbCliConsolePage extends Page {
private DsfSession fSession;
private Composite fMainComposite;
/** The control for the terminal widget embedded in the console */
private ITerminalViewControl fTerminalControl;
public GdbCliConsolePage(GdbCliConsole gdbConsole) {
ILaunch launch = gdbConsole.getLaunch();
if (launch instanceof GdbLaunch) {
fSession = ((GdbLaunch)launch).getSession();
} else {
assert false;
}
}
@Override
public void dispose() {
super.dispose();
fTerminalControl.disposeTerminal();
}
@Override
public void createControl(Composite parent) {
fMainComposite = new Composite(parent, SWT.NONE);
fMainComposite.setLayoutData(new GridData(GridData.FILL_BOTH));
fMainComposite.setLayout(new FillLayout());
// Create the terminal control that will be used to interact with GDB
fTerminalControl = TerminalViewControlFactory.makeControl(
new ITerminalListener() {
@Override public void setState(TerminalState state) {}
@Override public void setTerminalTitle(final String title) {}
},
fMainComposite,
new ITerminalConnector[] {},
true);
try {
fTerminalControl.setEncoding(Charset.defaultCharset().name());
} catch (UnsupportedEncodingException e) {
}
// Hook the terminal control to the GDB process
attachTerminalToGdbProcess();
}
@Override
public Control getControl() {
return fMainComposite;
}
@Override
public void setFocus() {
fTerminalControl.setFocus();
}
protected void attachTerminalToGdbProcess() {
if (fSession == null) {
return;
}
try {
fSession.getExecutor().submit(new DsfRunnable() {
@Override
public void run() {
DsfServicesTracker tracker = new DsfServicesTracker(GdbUIPlugin.getBundleContext(), fSession.getId());
IGDBBackend backend = tracker.getService(IGDBBackend.class);
tracker.dispose();
if (backend != null) {
if (backend.getProcess() != null) {
attachTerminal(backend.getProcess());
}
}
}
});
} catch (RejectedExecutionException e) {
}
}
protected void attachTerminal(Process process) {
ILauncherDelegate delegate =
LauncherDelegateManager.getInstance().getLauncherDelegate("org.eclipse.tm.terminal.connector.streams.launcher.streams", false); //$NON-NLS-1$
if (delegate != null) {
Map<String, Object> properties = createNewSettings(process);
ITerminalConnector connector = delegate.createTerminalConnector(properties);
fTerminalControl.setConnector(connector);
if (fTerminalControl instanceof ITerminalControl) {
((ITerminalControl)fTerminalControl).setConnectOnEnterIfClosed(false);
((ITerminalControl)fTerminalControl).setVT100LineWrapping(true);
}
// Must use syncExec because the logic within must complete before the rest
// of the class methods (specifically getProcess()) is called
fMainComposite.getDisplay().syncExec(new Runnable() {
@Override
public void run() {
if (fTerminalControl != null && !fTerminalControl.isDisposed()) {
fTerminalControl.clearTerminal();
fTerminalControl.connectTerminal();
}
}
});
}
}
protected Map<String, Object> createNewSettings(Process process) {
// Create the terminal connector
Map<String, Object> properties = new HashMap<String, Object>();
properties.put(ITerminalsConnectorConstants.PROP_LOCAL_ECHO, Boolean.FALSE);
properties.put(ITerminalsConnectorConstants.PROP_STREAMS_STDIN, process.getOutputStream());
properties.put(ITerminalsConnectorConstants.PROP_STREAMS_STDOUT, process.getInputStream());
properties.put(ITerminalsConnectorConstants.PROP_STREAMS_STDERR, process.getErrorStream());
properties.put(ITerminalsConnectorConstants.PROP_STDOUT_LISTENERS,
new ITerminalServiceOutputStreamMonitorListener[0]);
properties.put(ITerminalsConnectorConstants.PROP_STDERR_LISTENERS,
new ITerminalServiceOutputStreamMonitorListener[0]);
return properties;
}
}

View file

@ -138,7 +138,7 @@ public class TracingConsole extends IOConsole {
}
if (fLaunch.isTerminated()) {
return ConsoleMessages.ConsoleMessages_trace_console_terminated + label;
return ConsoleMessages.ConsoleMessages_console_terminated + label;
}
return label;

View file

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2010 Marc-Andre Laperle and others.
* Copyright (c) 2010, 2016 Marc-Andre Laperle 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
@ -10,13 +10,21 @@
*******************************************************************************/
package org.eclipse.cdt.dsf.gdb.launching;
import java.io.IOException;
import java.util.Map;
import org.eclipse.cdt.dsf.gdb.service.command.IGDBBackendProcessWithoutIO;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.model.IStreamMonitor;
import org.eclipse.debug.core.model.IStreamsProxy;
import org.eclipse.debug.core.model.RuntimeProcess;
/**
* A process for the gdb backend to differentiate it from the inferior
* A process for the gdb backend to differentiate it from the inferior.
*
* This process disables the base class handling of IO streams if we
* are using the full GDB console, which is handled by the
* GdbConsoleManager instead.
*
* @since 3.0
*/
@ -27,4 +35,56 @@ public class GDBProcess extends RuntimeProcess {
super(launch, process, name, attributes);
}
@Override
public IStreamsProxy getStreamsProxy() {
IStreamsProxy proxy = super.getStreamsProxy();
// If our proxy is the one that ignores the streams,
// this method should return null.
// Returning null insures that there will not be a
// text console automatically created for this process
// see ProcessConsoleManager#launchChanged()
return proxy instanceof NoStreamsProxy ? null : proxy;
}
@Override
protected IStreamsProxy createStreamsProxy() {
// TRICKY. This method is called by the constructor of
// the super class. This means we don't have time to
// set any fields in this class by then. Therefore,
// we can only use what was set by the base class constructor
// to figure out how to behave.
// We can call getSystemProcess() as it is set earlier
// in the constructor then when this method is called.
if (getSystemProcess() instanceof IGDBBackendProcessWithoutIO) {
// If the GDB process used does not handle I/O, we return a proxy
// that ignores the streams.
return new NoStreamsProxy();
}
return super.createStreamsProxy();
}
/**
* Class that provides a streams proxy that actually
* ignores the I/O streams. We use this in the case
* of the full GDB console where the GDB CLI is used directly,
* without us needing to do anything with the I/O ourselves.
*
* This is different than NullStreamsProxy which would
* still read but discard the IO, which is not what we want.
*/
private class NoStreamsProxy implements IStreamsProxy {
@Override
public IStreamMonitor getErrorStreamMonitor() {
return null;
}
@Override
public IStreamMonitor getOutputStreamMonitor() {
return null;
}
@Override
public void write(String input) throws IOException {
}
}
}

View file

@ -64,7 +64,6 @@ import org.eclipse.cdt.dsf.gdb.IGdbDebugPreferenceConstants;
import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin;
import org.eclipse.cdt.dsf.gdb.internal.memory.GdbMemoryBlockRetrievalManager;
import org.eclipse.cdt.dsf.gdb.service.command.IGDBControl;
import org.eclipse.cdt.dsf.mi.service.command.AbstractCLIProcess;
import org.eclipse.cdt.dsf.service.DsfServiceEventHandler;
import org.eclipse.cdt.dsf.service.DsfServicesTracker;
import org.eclipse.cdt.dsf.service.DsfSession;
@ -202,13 +201,13 @@ public class GdbLaunch extends DsfLaunch implements ITerminate, IDisconnect, ITr
@ThreadSafeAndProhibitedFromDsfExecutor("getDsfExecutor()")
public void addCLIProcess(String label) throws CoreException {
try {
// Add the CLI process object to the launch.
AbstractCLIProcess cliProc = getDsfExecutor().submit(new Callable<AbstractCLIProcess>() {
// Add the GDB process object to the launch.
Process gdbProc = getDsfExecutor().submit(new Callable<Process>() {
@Override
public AbstractCLIProcess call() throws CoreException {
public Process call() throws CoreException {
IGDBControl gdb = fTracker.getService(IGDBControl.class);
if (gdb != null) {
return gdb.getCLIProcess();
return gdb.getGDBBackendProcess();
}
return null;
}
@ -221,7 +220,7 @@ public class GdbLaunch extends DsfLaunch implements ITerminate, IDisconnect, ITr
Map<String, String> attributes = new HashMap<String, String>();
attributes.put(IGdbDebugConstants.PROCESS_TYPE_CREATION_ATTR,
IGdbDebugConstants.GDB_PROCESS_CREATION_VALUE);
DebugPlugin.newProcess(this, cliProc, label, attributes);
DebugPlugin.newProcess(this, gdbProc, label, attributes);
} catch (InterruptedException e) {
throw new CoreException(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, 0,
"Interrupted while waiting for get process callable.", e)); //$NON-NLS-1$

View file

@ -157,7 +157,8 @@ public class GDBBackend extends AbstractDsfService implements IGDBBackend, IMIBa
};
}
private GdbLaunch getGDBLaunch() {
/** @since 5.1 */
protected GdbLaunch getGDBLaunch() {
return (GdbLaunch) getSession().getModelAdapter(ILaunch.class);
}
@ -171,7 +172,9 @@ public class GDBBackend extends AbstractDsfService implements IGDBBackend, IMIBa
* array. Allow subclass to override.
*
* @since 4.6
* @deprecated Replaced by getDebuggerCommandLine()
*/
@Deprecated
protected String[] getGDBCommandLineArray() {
// The goal here is to keep options to an absolute minimum.
// All configuration should be done in the final launch sequence
@ -189,6 +192,39 @@ public class GDBBackend extends AbstractDsfService implements IGDBBackend, IMIBa
return CommandLineUtil.argumentsToArray(cmd);
}
/**
* Returns the GDB command and its arguments as an array.
* Allow subclass to override.
* @since 5.1
*/
// This method replaces getGDBCommandLineArray() because we need
// to override it for GDB 7.12 even if an extender has overridden
// getGDBCommandLineArray().
// Here is the scenario:
// An extender has overridden getGDBCommandLineArray() to launch
// GDB in MI mode but with extra parameters. Once GDBBackend_7_12
// is released, the extender may likely point their extension to
// GDBBackend_7_12 instead of GDBBackend (which will even happen
// automatically if the extender extends GDBBackend_HEAD).
// In such a case, they would override the changes in
// GDBBackend_7_12.getGDBCommandLineArray() and the debug session
// is likely to fail since with GDBBackend_7_12, we launch GDB
// in CLI mode.
//
// Instead, we use getDebuggerCommandLine() and override that method in
// GDBBackend_7_12. That way an extender will not override it
// without noticing (since it didn't exist before). Then we can call
// the overridden getGDBCommandLineArray() and work with that to
// make it work with the new way to launch GDB of GDBBackend_7_12
//
// Note that we didn't name this method getGDBCommandLine() because
// this name had been used in CDT 8.8 and could still be part of
// extenders' code.
protected String[] getDebuggerCommandLine() {
// Call the old method in case it was overridden
return getGDBCommandLineArray();
}
@Override
public String getGDBInitFile() throws CoreException {
return getGDBLaunch().getGDBInitFile();
@ -236,12 +272,35 @@ public class GDBBackend extends AbstractDsfService implements IGDBBackend, IMIBa
return getGDBLaunch().getUpdateThreadListOnSuspend();
}
/**
* Launch GDB process. Allow subclass to override.
*
* @since 5.1
*/
// Again, we create a new method that we know has not been already
// overridden. That way, even if extenders have overridden the
// original launchGDBProcess(String[]), we will instead use
// the GDBBackend_7_12#launchGDBProcess() method when appropriate.
// This is important because if we didn't, the new console would
// not work properly.
//
// Of course, in that case, we won't call the extenders overridden
// launchGDBProcess(String[]) and therefore will not get their
// specialized code. I feel this is still a lower risk than
// not starting the full GDB console properly.
protected Process launchGDBProcess() throws CoreException {
// Call the old method in case it was overridden
return launchGDBProcess(getDebuggerCommandLine());
}
/**
* Launch GDB process with command and arguments. Allow subclass to
* override.
*
* @since 4.6
* @deprecated Replace by launchGDBProcess()
*/
@Deprecated
protected Process launchGDBProcess(String[] commandLine) throws CoreException {
Process proc = null;
try {
@ -254,6 +313,7 @@ public class GDBBackend extends AbstractDsfService implements IGDBBackend, IMIBa
return proc;
}
@Override
public Process getProcess() {
return fProcess;
}
@ -466,7 +526,8 @@ public class GDBBackend extends AbstractDsfService implements IGDBBackend, IMIBa
}
try {
fProcess = launchGDBProcess(getGDBCommandLineArray());
fProcess = launchGDBProcess();
// Need to do this on the executor for thread-safety
getExecutor().submit(new DsfRunnable() {
@Override
@ -488,8 +549,12 @@ public class GDBBackend extends AbstractDsfService implements IGDBBackend, IMIBa
BufferedReader errorReader = null;
boolean success = false;
try {
// Must call getMIInputStream() because we always want to read from the MI stream,
// which is not always the same as the input stream of fProcess. They are
// different when we use the full GDB console
InputStream inputStream = getMIInputStream();
// Read initial GDB prompt
inputReader = new BufferedReader(new InputStreamReader(getMIInputStream()));
inputReader = new BufferedReader(new InputStreamReader(inputStream));
String line;
while ((line = inputReader.readLine()) != null) {
line = line.trim();
@ -501,7 +566,11 @@ public class GDBBackend extends AbstractDsfService implements IGDBBackend, IMIBa
// Failed to read initial prompt, check for error
if (!success) {
errorReader = new BufferedReader(new InputStreamReader(getMIErrorStream()));
// Don't call getMIErrorStream() because it can be overridden with a
// dummy stream in the case of the full GDB console.
// Instead, make sure we read the error from the process itself.
InputStream errorStream = fProcess.getErrorStream();
errorReader = new BufferedReader(new InputStreamReader(errorStream));
String errorInfo = errorReader.readLine();
if (errorInfo == null) {
errorInfo = "GDB prompt not read"; //$NON-NLS-1$

View file

@ -0,0 +1,191 @@
/*******************************************************************************
* Copyright (c) 2016 Ericsson 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
*******************************************************************************/
package org.eclipse.cdt.dsf.gdb.service;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import org.eclipse.cdt.core.parser.util.StringUtil;
import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin;
import org.eclipse.cdt.dsf.service.DsfSession;
import org.eclipse.cdt.utils.pty.PTY;
import org.eclipse.cdt.utils.pty.PTY.Mode;
import org.eclipse.cdt.utils.spawner.ProcessFactory;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.osgi.util.NLS;
/**
* Implementation of {@link IGDBBackend} using GDB 7.12. This version provides
* full GDB console support. It achieves this by launching GDB in CLI mode
* in a special console widget and then connecting to GDB via MI by telling GDB to
* open a new MI console. The rest of the DSF-GDB support then stays the same.
*
* If we are unable to create a PTY, we then revert to the previous behavior of
* the base class.
*
* @since 5.1
*/
public class GDBBackend_7_12 extends GDBBackend {
/** The PTY that is used to enable the full GDB console */
private PTY fPty;
/** Indicate that we failed to create a PTY. */
private boolean fPtyFailure;
private InputStream fDummyErrorStream;
public GDBBackend_7_12(DsfSession session, ILaunchConfiguration lc) {
super(session, lc);
createPty();
}
@Override
public boolean isFullGdbConsoleSupported() {
return !Platform.getOS().equals(Platform.OS_WIN32)
&& !fPtyFailure;
}
protected void createPty() {
if (!isFullGdbConsoleSupported()) {
return;
}
try {
fPty = new PTY();
fPty.validateSlaveName();
// With the PTY the stderr is redirected to the PTY's output stream.
// Therefore, return a dummy stream for the error stream.
fDummyErrorStream = new InputStream() {
@Override
public int read() throws IOException {
return -1;
}
};
} catch (IOException e) {
fPty = null;
fPtyFailure = true;
GdbPlugin.log(new Status(
IStatus.INFO, GdbPlugin.PLUGIN_ID,
NLS.bind(Messages.PTY_Console_not_available, e.getMessage())));
}
}
@Override
public OutputStream getMIOutputStream() {
if (fPty == null) {
return super.getMIOutputStream();
}
return fPty.getOutputStream();
};
@Override
public InputStream getMIInputStream() {
if (fPty == null) {
return super.getMIInputStream();
}
return fPty.getInputStream();
};
@Override
public InputStream getMIErrorStream() {
if (fPty == null) {
return super.getMIErrorStream();
}
return fDummyErrorStream;
};
@Override
protected String[] getDebuggerCommandLine() {
// Start from the original command line method which
// could have been overridden by extenders, and add what we need
// to convert it to a command that will launch in CLI mode.
// Then trigger the MI console
@SuppressWarnings("deprecation")
String[] originalCommandLine = getGDBCommandLineArray();
if (!isFullGdbConsoleSupported()) {
return originalCommandLine;
}
// Below are the parameters we need to add to an existing commandLine,
// to trigger a launch with the full CLI. This would also work
// as the only parameters for a full CLI launch (although "--interpreter console"
// could be removed in that case)
String[] extraArguments = new String[] {
// Start with -q option to avoid extra output which may trigger pagination
// This is important because if pagination is triggered on the version
// printout, we won't be able to send the command to start the MI channel.
// Note that we cannot turn off pagination early enough to prevent the
// original version output from paginating
"-q", //$NON-NLS-1$
// We don't put --nx at this time because our base class puts it already and if
// if an extender has removed it, we shouldn't add it again.
// Once we no longer extends the deprecated getGDBCommandLineArray() and simply
// create the full commandLine here, we should put it
// // Use the --nx option to avoid reading the gdbinit file here.
// // The gdbinit file is read explicitly in the FinalLaunchSequence to make
// // it easier to customize.
// "--nx", //$NON-NLS-1$
// Force a CLI console since the originalCommandLine
// probably specified "-i mi" or "--interpreter mi"
// Once we no longer extend the deprecated
// getGDBCommandLineArray() and simply create the full
// commandLine here, we could remove this parameter
"--interpreter", "console", //$NON-NLS-1$ //$NON-NLS-2$
// Now trigger the new console towards our PTY.
"-ex", "new-ui mi " + fPty.getSlaveName(), //$NON-NLS-1$ //$NON-NLS-2$
// Now print the version so the user gets that familiar output
"-ex", "show version" //$NON-NLS-1$ //$NON-NLS-2$
};
int oriLength = originalCommandLine.length;
int extraLength = extraArguments.length;
String[] newCommandLine = new String[oriLength+extraLength];
System.arraycopy(originalCommandLine, 0, newCommandLine, 0, oriLength);
System.arraycopy(extraArguments, 0, newCommandLine, oriLength, extraLength);
return newCommandLine;
}
@Override
protected Process launchGDBProcess() throws CoreException {
if (!isFullGdbConsoleSupported()) {
return super.launchGDBProcess();
}
// If we are launching the full console, we need to use a PTY in TERMINAL mode
// for the GDB CLI to properly display in its view
Process proc = null;
String[] commandLine = getDebuggerCommandLine();
try {
IPath path = getGDBWorkingDirectory();
proc = ProcessFactory.getFactory().exec(
commandLine,
getGDBLaunch().getLaunchEnvironment(),
new File(path != null ? path.toOSString() : ""), //$NON-NLS-1$
new PTY(Mode.TERMINAL));
} catch (IOException e) {
String message = "Error while launching command: " + StringUtil.join(commandLine, " "); //$NON-NLS-1$ //$NON-NLS-2$
throw new CoreException(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, message, e));
}
return proc;
}
}

View file

@ -88,6 +88,8 @@ public class GdbDebugServicesFactory extends AbstractDsfDebugServicesFactory {
public static final String GDB_7_7_VERSION = "7.7"; //$NON-NLS-1$
/** @since 4.8 */
public static final String GDB_7_10_VERSION = "7.10"; //$NON-NLS-1$
/** @since 5.1 */
public static final String GDB_7_12_VERSION = "7.12"; //$NON-NLS-1$
private final String fVersion;
private final ILaunchConfiguration fConfiguration;
@ -224,6 +226,11 @@ public class GdbDebugServicesFactory extends AbstractDsfDebugServicesFactory {
}
protected IMIBackend createBackendGDBService(DsfSession session, ILaunchConfiguration lc) {
if (compareVersionWith(GDB_7_12_VERSION) >= 0
|| compareVersionWith("7.11.50") >= 0 // TODO remove once GDB 7.12 is released
) {
return new GDBBackend_7_12(session, lc);
}
return new GDBBackend(session, lc);
}

View file

@ -156,4 +156,21 @@ public interface IGDBBackend extends IMIBackend {
* @since 3.0
*/
public boolean getUpdateThreadListOnSuspend() throws CoreException;
/**
* @return True if the full GDB console should be used. False otherwise.
*
* @since 5.1
*/
default boolean isFullGdbConsoleSupported() {
return false;
}
/**
* @return The real GDB process that was started for the debug session
* @since 5.1
*/
default Process getProcess() {
throw new RuntimeException();
}
}

View file

@ -31,6 +31,7 @@ class Messages extends NLS {
public static String RegisterGroup_name_used;
public static String RegisterGroup_invalid_number_of_registers;
public static String GDB_Version_Mismatch;
public static String PTY_Console_not_available;
static {
// initialize resource bundle

View file

@ -22,4 +22,5 @@ ErrorNotSupported=Operation not supported with this GDB version
RegisterGroup_name_reserved=The group name "{0}" is reserved
RegisterGroup_name_used=The group name "{0}" is already in use
RegisterGroup_invalid_number_of_registers=A minimum of one register is needed for a Register Group
GDB_Version_Mismatch=Running older GDB version {0} when service {1} expects version {2} or higher
GDB_Version_Mismatch=Running older GDB version {0} when service {1} expects version {2} or higher
PTY_Console_not_available=PTY not available, cannot use full GDB console: {0}

View file

@ -21,7 +21,12 @@ import org.eclipse.cdt.dsf.mi.service.IMIBackend;
import org.eclipse.cdt.dsf.mi.service.command.MIBackendCLIProcess;
import org.eclipse.cdt.dsf.service.DsfSession;
/**
/**
* Note that starting with GDB 7.12, as long as a PTY is available,
* this process is no longer used. Instead, the real GDB process,
* along with its console will be used directly. A second PTY
* will be used to communicate using MI.
* @author LWang
* @since 2.0
*

View file

@ -0,0 +1,44 @@
/*******************************************************************************
* Copyright (c) 2016 Ericsson 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
*******************************************************************************/
package org.eclipse.cdt.dsf.gdb.service.command;
import java.io.IOException;
import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService;
import org.eclipse.cdt.dsf.mi.service.IMIBackend;
/**
* Note that starting with GDB 7.12, as long as a PTY is available, this process
* is used instead of GDBBackendProcess. This is because the GDB CLI is handled
* directly by GDB and the current class only needs to handle the life-cycle of
* the GDB process.
*
* This class is therefore a representation of the GDB process that will be
* added to the launch. This class is not the real GDB process but simply an
* entry for the launch to handle user actions but no IO.
*
* This class extends {@link GDBBackendCLIProcess} to re-use its implementation
* of the {@link Process} abstract methods, but disables all I/O and
* local CLI handling.
*
* @since 5.1
*/
public class GDBBackendProcessWithoutIO extends GDBBackendCLIProcess implements IGDBBackendProcessWithoutIO {
public GDBBackendProcessWithoutIO(ICommandControlService commandControl, IMIBackend backend) throws IOException {
super(commandControl, backend);
}
@Override
public boolean handleIO() {
// Streams are handled directly by the real process.
// This class is just representation for the launch, without IO.
return false;
}
}

View file

@ -193,7 +193,7 @@ public class GDBControl extends AbstractMIControl implements IGDBControl {
private IEventProcessor fMIEventProcessor;
private IEventProcessor fCLICommandProcessor;
private IEventProcessor fControlEventProcessor;
private AbstractCLIProcess fCLIProcess;
private Process fBackendProcess;
private GdbCommandTimeoutManager fCommandTimeoutManager;
@ -358,11 +358,24 @@ public class GDBControl extends AbstractMIControl implements IGDBControl {
);
}
/**
* @deprecated Replaced by {@link #getGDBBackendProcess()}
*/
@Deprecated
@Override
public AbstractCLIProcess getCLIProcess() {
return fCLIProcess;
public AbstractCLIProcess getCLIProcess() {
if (fBackendProcess instanceof AbstractCLIProcess) {
return (AbstractCLIProcess)fBackendProcess;
}
return null;
}
/** @since 5.1 */
@Override
public Process getGDBBackendProcess() {
return fBackendProcess;
}
/**
* @since 2.0
*/
@ -546,8 +559,8 @@ public class GDBControl extends AbstractMIControl implements IGDBControl {
/** @since 5.1 */
protected void doCommandProcessorsStep(final RequestMonitor requestMonitor) {
try {
fCLIProcess = new GDBBackendCLIProcess(GDBControl.this, fMIBackend);
try {
fBackendProcess = createBackendProcess();
}
catch(IOException e) {
requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.REQUEST_FAILED, "Failed to create CLI Process", e)); //$NON-NLS-1$
@ -567,7 +580,9 @@ public class GDBControl extends AbstractMIControl implements IGDBControl {
fControlEventProcessor.dispose();
fCLICommandProcessor.dispose();
fMIEventProcessor.dispose();
fCLIProcess.dispose();
if (fBackendProcess instanceof AbstractCLIProcess) {
((AbstractCLIProcess)fBackendProcess).dispose();
}
requestMonitor.done();
}
@ -715,6 +730,18 @@ public class GDBControl extends AbstractMIControl implements IGDBControl {
return new ControlEventProcessor();
}
/** @since 5.1 */
protected Process createBackendProcess() throws IOException {
if (fMIBackend.isFullGdbConsoleSupported()) {
// If the full GDB console is supported, which uses the GDB process itself,
// we return a GDBBackendProcess that does not take care of I/O
return new GDBBackendProcessWithoutIO(this, fMIBackend);
}
// If the full GDB console is not supported according to the backend service,
// then we create a special GDBBackendProcess that handles the CLI
return new GDBBackendCLIProcess(this, fMIBackend);
}
/**
* @since 4.1
*/

View file

@ -0,0 +1,22 @@
/*******************************************************************************
* Copyright (c) 2016 Ericsson and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*******************************************************************************/
package org.eclipse.cdt.dsf.gdb.service.command;
/**
* Interface used by a process representing the GDB process but for which there
* is no IO.
*
* When using the full GDB console, this marker can be used for the class that
* will represent the GDB process in the launch since the IO should not be
* handled by the launch and the console it normally created, but is handled by
* the full GDB console itself.
*
* @since 5.1
*/
public interface IGDBBackendProcessWithoutIO {
}

View file

@ -17,11 +17,37 @@ import java.util.List;
import java.util.Properties;
import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
import org.eclipse.cdt.dsf.gdb.service.IGDBBackend;
import org.eclipse.cdt.dsf.mi.service.IMICommandControl;
import org.eclipse.cdt.dsf.mi.service.command.AbstractCLIProcess;
public interface IGDBControl extends IMICommandControl {
/**
* Returns the process that represents GDB.
* This is the process that should be added to the launch.
* Note that this is usually not be the actual GDB process but
* only one that is used to represent it.
* To get the real GDB process use
* {@link IGDBBackend#getProcess()}.
*
* @since 5.1
*/
default Process getGDBBackendProcess() {
return getCLIProcess();
};
/**
* @deprecated The return value of this method was too
* restrictive. It has been replaced with
* {@link #getGDBBackendProcess()}
* @return The AbstractCLIProcess that handles the CLI.
* Will return null if the CLI is not handled
* by an AbstractCLIProcess; this will sometimes
* happen for GDB >= 7.12 if the CLI is handled
* by the GDB process itself.
*/
@Deprecated
AbstractCLIProcess getCLIProcess();
/**

View file

@ -7,7 +7,7 @@
*******************************************************************************/
package org.eclipse.cdt.dsf.gdb.service.extensions;
import org.eclipse.cdt.dsf.gdb.service.GDBBackend;
import org.eclipse.cdt.dsf.gdb.service.GDBBackend_7_12;
import org.eclipse.cdt.dsf.gdb.service.GdbDebugServicesFactory;
import org.eclipse.cdt.dsf.mi.service.IMIBackend;
import org.eclipse.cdt.dsf.service.DsfSession;
@ -37,13 +37,13 @@ import org.eclipse.debug.core.ILaunchConfiguration;
*
* @since 4.8
*/
public class GDBBackend_HEAD extends GDBBackend {
public class GDBBackend_HEAD extends GDBBackend_7_12 {
public GDBBackend_HEAD(DsfSession session, ILaunchConfiguration lc) {
super(session, lc);
validateGdbVersion(session);
}
protected String getMinGDBVersionSupported() { return GdbDebugServicesFactory.GDB_7_1_VERSION; }
protected String getMinGDBVersionSupported() { return GdbDebugServicesFactory.GDB_7_12_VERSION; }
protected void validateGdbVersion(DsfSession session) {
GdbDebugServicesFactory.validateGdbVersion(session, getMinGDBVersionSupported(), this);

View file

@ -46,9 +46,14 @@ import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
/**
* This Process implementation tracks the process the GDB process. This
* process object is displayed in Debug view and is used to
* accept CLI commands and to write their output to the console.
* This Process implementation tracks the GDB process. This
* process object is displayed in the Debug view and is used to
* accept CLI commands and to write their output to the console.
*
* Starting with GDB 7.12, as long as a PTY is available,
* this process is no longer used. Instead, the real GDB process,
* along with its console will be used directly. A second PTY
* will be used to communicate using MI.
*
* @see org.eclipse.debug.core.model.IProcess
*/
@ -64,7 +69,7 @@ public abstract class AbstractCLIProcess extends Process
private final DsfSession fSession;
private final ICommandControlService fCommandControl;
private final OutputStream fOutputStream = new CLIOutputStream();
private OutputStream fOutputStream;
// Client process console stream.
private PipedInputStream fMIInConsolePipe;
@ -103,34 +108,42 @@ public abstract class AbstractCLIProcess extends Process
fSession = commandControl.getSession();
fCommandControl = commandControl;
commandControl.addEventListener(this);
commandControl.addCommandListener(this);
if (handleIO()) {
fOutputStream = new CLIOutputStream();
PipedInputStream miInConsolePipe = null;
PipedOutputStream miOutConsolePipe = null;
PipedInputStream miInLogPipe = null;
PipedOutputStream miOutLogPipe = null;
try {
// Using a LargePipedInputStream see https://bugs.eclipse.org/bugs/show_bug.cgi?id=223154
miOutConsolePipe = new PipedOutputStream();
miInConsolePipe = new LargePipedInputStream(miOutConsolePipe);
miOutLogPipe = new PipedOutputStream();
miInLogPipe = new LargePipedInputStream(miOutLogPipe);
} catch (IOException e) {
ILog log = GdbPlugin.getDefault().getLog();
if (log != null) {
log.log(new Status(
IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Error when creating log pipes", e)); //$NON-NLS-1$
}
commandControl.addEventListener(this);
commandControl.addCommandListener(this);
PipedInputStream miInConsolePipe = null;
PipedOutputStream miOutConsolePipe = null;
PipedInputStream miInLogPipe = null;
PipedOutputStream miOutLogPipe = null;
try {
// Using a LargePipedInputStream see https://bugs.eclipse.org/bugs/show_bug.cgi?id=223154
miOutConsolePipe = new PipedOutputStream();
miInConsolePipe = new LargePipedInputStream(miOutConsolePipe);
miOutLogPipe = new PipedOutputStream();
miInLogPipe = new LargePipedInputStream(miOutLogPipe);
} catch (IOException e) {
ILog log = GdbPlugin.getDefault().getLog();
if (log != null) {
log.log(new Status(
IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Error when creating log pipes", e)); //$NON-NLS-1$
}
}
fMIOutConsolePipe = miOutConsolePipe;
fMIInConsolePipe = miInConsolePipe;
fMIOutLogPipe = miOutLogPipe;
fMIInLogPipe = miInLogPipe;
}
// Must initialize these outside of the try block because they are final.
fMIOutConsolePipe = miOutConsolePipe;
fMIInConsolePipe = miInConsolePipe;
fMIOutLogPipe = miOutLogPipe;
fMIInLogPipe = miInLogPipe;
}
/**
* @since 5.1
*/
protected boolean handleIO() { return true; }
protected DsfSession getSession() { return fSession; }
/**
@ -165,18 +178,26 @@ public abstract class AbstractCLIProcess extends Process
}
private void closeIO() {
try {
fMIOutConsolePipe.close();
} catch (IOException e) {}
try {
fMIInConsolePipe.close();
} catch (IOException e) {}
try {
fMIOutLogPipe.close();
} catch (IOException e) {}
try {
fMIInLogPipe.close();
} catch (IOException e) {}
if (fMIOutConsolePipe != null) {
try {
fMIOutConsolePipe.close();
} catch (IOException e) {}
}
if (fMIInConsolePipe != null) {
try {
fMIInConsolePipe.close();
} catch (IOException e) {}
}
if (fMIOutLogPipe != null) {
try {
fMIOutLogPipe.close();
} catch (IOException e) {}
}
if (fMIInLogPipe != null) {
try {
fMIInLogPipe.close();
} catch (IOException e) {}
}
}

View file

@ -37,6 +37,11 @@ import org.eclipse.core.runtime.Status;
/**
* CLI Process object implementation which uses the {@link IMIBackend} service
* to monitor and control the underlying process.
*
* Note that starting with GDB 7.12, as long as a PTY is available,
* this process is no longer used. Instead, the real GDB process,
* along with its console will be used directly. A second PTY
* will be used to communicate using MI.
*
* @since 1.1
*/

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?pde version="3.8"?><target name="cdt" sequenceNumber="14">
<?pde version="3.8"?><target name="cdt" sequenceNumber="15">
<locations>
<location includeAllPlatforms="false" includeConfigurePhase="false" includeMode="planner" includeSource="true" type="InstallableUnit">
<unit id="org.apache.commons.compress" version="0.0.0"/>
@ -60,6 +60,7 @@
</location>
<location includeAllPlatforms="false" includeConfigurePhase="false" includeMode="planner" includeSource="true" type="InstallableUnit">
<unit id="org.eclipse.tm.terminal.control" version="0.0.0"/>
<unit id="org.eclipse.tm.terminal.view.ui" version="0.0.0"/>
<repository location="http://download.eclipse.org/tm/terminal/builds/development/nightly/"/>
</location>
<location includeAllPlatforms="false" includeConfigurePhase="false" includeMode="planner" includeSource="true" type="InstallableUnit">