diff options
28 files changed, 1072 insertions, 77 deletions
diff --git a/debug/org.eclipse.cdt.debug.application.product/debug.product b/debug/org.eclipse.cdt.debug.application.product/debug.product index fe8724b97ea..cab34794d25 100644 --- a/debug/org.eclipse.cdt.debug.application.product/debug.product +++ b/debug/org.eclipse.cdt.debug.application.product/debug.product @@ -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"/> diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/META-INF/MANIFEST.MF b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/META-INF/MANIFEST.MF index c60bc3436ba..35449285435 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/META-INF/MANIFEST.MF +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/META-INF/MANIFEST.MF @@ -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", diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/plugin.xml b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/plugin.xml index 2afc82dc711..7d0180058cb 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/plugin.xml +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/plugin.xml @@ -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> diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/GdbUIPlugin.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/GdbUIPlugin.java index 1bea161ab4a..5468fa6045c 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/GdbUIPlugin.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/GdbUIPlugin.java @@ -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; diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/console/ConsoleMessages.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/console/ConsoleMessages.java index 059e39b039b..d9e294d7130 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/console/ConsoleMessages.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/console/ConsoleMessages.java @@ -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; diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/console/ConsoleMessages.properties b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/console/ConsoleMessages.properties index 5aafada28d0..7d098d352f9 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/console/ConsoleMessages.properties +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/console/ConsoleMessages.properties @@ -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 diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/console/ConsolePageParticipant.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/console/ConsolePageParticipant.java index a8ff90be11a..73b3dfa60db 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/console/ConsolePageParticipant.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/console/ConsolePageParticipant.java @@ -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); diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/console/GdbCliConsole.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/console/GdbCliConsole.java new file mode 100644 index 00000000000..68f1e820803 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/console/GdbCliConsole.java @@ -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); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/console/GdbCliConsoleManager.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/console/GdbCliConsoleManager.java new file mode 100644 index 00000000000..b61a7b194e4 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/console/GdbCliConsoleManager.java @@ -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); + } + } + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/console/GdbCliConsolePage.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/console/GdbCliConsolePage.java new file mode 100644 index 00000000000..c3600129d85 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/console/GdbCliConsolePage.java @@ -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; + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/console/TracingConsole.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/console/TracingConsole.java index e65bff0c98f..a60cab216f5 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/console/TracingConsole.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/console/TracingConsole.java @@ -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; diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/GDBProcess.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/GDBProcess.java index b36c99f117f..22cf639c5fd 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/GDBProcess.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/GDBProcess.java @@ -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 { + } + } } diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/GdbLaunch.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/GdbLaunch.java index 6abe37d0eef..48ec7751db1 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/GdbLaunch.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/GdbLaunch.java @@ -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$ diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBBackend.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBBackend.java index 94ebb852dbe..d2fa6815355 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBBackend.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBBackend.java @@ -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(); @@ -237,11 +273,34 @@ public class GDBBackend extends AbstractDsfService implements IGDBBackend, IMIBa } /** + * 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$ diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBBackend_7_12.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBBackend_7_12.java new file mode 100644 index 00000000000..edb881e87ca --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBBackend_7_12.java @@ -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; + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GdbDebugServicesFactory.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GdbDebugServicesFactory.java index e9814b248cc..1968421f67b 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GdbDebugServicesFactory.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GdbDebugServicesFactory.java @@ -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); } diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/IGDBBackend.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/IGDBBackend.java index abbc9844869..4dccacc137d 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/IGDBBackend.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/IGDBBackend.java @@ -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(); + } } diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/Messages.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/Messages.java index da5bc459d7a..a6657cce834 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/Messages.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/Messages.java @@ -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 diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/Messages.properties b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/Messages.properties index dd6e0eff46e..d2c0fd6f97e 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/Messages.properties +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/Messages.properties @@ -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
\ No newline at end of file +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}
\ No newline at end of file diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/command/GDBBackendCLIProcess.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/command/GDBBackendCLIProcess.java index 3a07602ea84..0cec1bbc1c4 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/command/GDBBackendCLIProcess.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/command/GDBBackendCLIProcess.java @@ -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 * diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/command/GDBBackendProcessWithoutIO.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/command/GDBBackendProcessWithoutIO.java new file mode 100644 index 00000000000..0345b140954 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/command/GDBBackendProcessWithoutIO.java @@ -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; + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/command/GDBControl.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/command/GDBControl.java index 51d414b572a..1d751cb7b87 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/command/GDBControl.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/command/GDBControl.java @@ -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 */ diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/command/IGDBBackendProcessWithoutIO.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/command/IGDBBackendProcessWithoutIO.java new file mode 100644 index 00000000000..147e7a08a23 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/command/IGDBBackendProcessWithoutIO.java @@ -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 { +}
\ No newline at end of file diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/command/IGDBControl.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/command/IGDBControl.java index 2e149156b78..afbdc782304 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/command/IGDBControl.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/command/IGDBControl.java @@ -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(); /** diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/extensions/GDBBackend_HEAD.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/extensions/GDBBackend_HEAD.java index dbf43752405..75353aafa65 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/extensions/GDBBackend_HEAD.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/extensions/GDBBackend_HEAD.java @@ -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); diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/AbstractCLIProcess.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/AbstractCLIProcess.java index eabfcfbac51..adbb1a494f0 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/AbstractCLIProcess.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/AbstractCLIProcess.java @@ -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) {} + } } diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/MIBackendCLIProcess.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/MIBackendCLIProcess.java index a7091a0945f..f2dcb01f69c 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/MIBackendCLIProcess.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/MIBackendCLIProcess.java @@ -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 */ diff --git a/releng/org.eclipse.cdt.target/cdt.target b/releng/org.eclipse.cdt.target/cdt.target index 5d2df286e14..b7e1a52c59a 100644 --- a/releng/org.eclipse.cdt.target/cdt.target +++ b/releng/org.eclipse.cdt.target/cdt.target @@ -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"> |