Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: a1255e2d357e38f4093168c5222445b9debb5fea (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
/*******************************************************************************
 * Copyright (c) 2016 Ericsson and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *******************************************************************************/
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) && !Platform.getOS().equals(Platform.OS_MACOSX)
				&& !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;
	}
}

Back to the top