/******************************************************************************* * 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.2 */ public class GDBBackend_7_12 extends GDBBackend { /** The PTY that is used to create the MI channel */ private PTY fMIPty; /** The PTY that is used to create the GDB process in CLI mode */ private PTY fCLIPty; /** 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 { fMIPty = new PTY(); fMIPty.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) { fMIPty = 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 (fMIPty == null) { return super.getMIOutputStream(); } return fMIPty.getOutputStream(); }; @Override public InputStream getMIInputStream() { if (fMIPty == null) { return super.getMIInputStream(); } return fMIPty.getInputStream(); }; @Override public InputStream getMIErrorStream() { if (fMIPty == 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 " + fMIPty.getSlaveName(), //$NON-NLS-1$ //$NON-NLS-2$ // With GDB.7.12, pagination can lock up the whole debug session // when using the full GDB console, so we turn it off. // We must turn it off before calling 'show version' as even // that command could cause pagination to trigger "-ex", "set pagination off", //$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 { fCLIPty = new PTY(Mode.TERMINAL); IPath path = getGDBWorkingDirectory(); proc = ProcessFactory.getFactory().exec( commandLine, getGDBLaunch().getLaunchEnvironment(), new File(path != null ? path.toOSString() : ""), //$NON-NLS-1$ fCLIPty); } 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; } @Override public PTY getProcessPty() { return fCLIPty; } }