Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 26fd546412df3537bfe1dbf8cb4a7d5bea069f77 (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
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
/*******************************************************************************
 * Copyright (c) 2011, 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
 *
 * Contributors:
 *     Ericsson - Initial API and implementation 
 *     Marc Khouzam (Ericsson) - Support for fast tracepoints (Bug 346320)
 *     Marc Khouzam (Ericsson) - Fetch groupIds when getting breakpoints (Bug 360735)
 *******************************************************************************/

package org.eclipse.cdt.dsf.gdb.service;

import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;

import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
import org.eclipse.cdt.dsf.concurrent.ImmediateDataRequestMonitor;
import org.eclipse.cdt.dsf.concurrent.ImmediateRequestMonitor;
import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
import org.eclipse.cdt.dsf.datamodel.IDMContext;
import org.eclipse.cdt.dsf.debug.service.IBreakpoints;
import org.eclipse.cdt.dsf.debug.service.IBreakpointsExtension;
import org.eclipse.cdt.dsf.gdb.IGDBLaunchConfigurationConstants;
import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin;
import org.eclipse.cdt.dsf.mi.service.IMICommandControl;
import org.eclipse.cdt.dsf.mi.service.MIBreakpointDMData;
import org.eclipse.cdt.dsf.mi.service.MIBreakpoints;
import org.eclipse.cdt.dsf.mi.service.command.output.CLIInfoBreakInfo;
import org.eclipse.cdt.dsf.mi.service.command.output.MIBreakInsertInfo;
import org.eclipse.cdt.dsf.mi.service.command.output.MIBreakListInfo;
import org.eclipse.cdt.dsf.mi.service.command.output.MIBreakpoint;
import org.eclipse.cdt.dsf.service.DsfSession;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.debug.core.ILaunch;

/**
 * Breakpoint service for GDB 7.2.
 * It support MI for tracepoints.
 * It also support for fast vs normal tracepoints.
 *
 * @since 4.1
 */
public class GDBBreakpoints_7_2 extends GDBBreakpoints_7_0
{
	private IMICommandControl fConnection;
	
	private enum TracepointMode { FAST_THEN_NORMAL, FAST_ONLY, NORMAL_ONLY };
	   
	private TracepointMode fTracepointMode = TracepointMode.NORMAL_ONLY;

	public GDBBreakpoints_7_2(DsfSession session) {
		super(session);
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.dsf.service.AbstractDsfService#initialize(org.eclipse.cdt.dsf.concurrent.RequestMonitor)
	 */
	@Override
	public void initialize(final RequestMonitor rm) {
		super.initialize(new ImmediateRequestMonitor(rm) {
			@Override
			protected void handleSuccess() {
				doInitialize(rm);
			}
		});
	}

	private void doInitialize(final RequestMonitor rm) {
    	// Get the services references
		fConnection = getServicesTracker().getService(IMICommandControl.class);

		setTracepointMode();
		
		// Register this service
		register(new String[] { IBreakpoints.class.getName(),
		                        IBreakpointsExtension.class.getName(),
								MIBreakpoints.class.getName(),
								GDBBreakpoints_7_0.class.getName(),
								GDBBreakpoints_7_2.class.getName() },
				new Hashtable<String, String>());

		rm.done();
	}

	@Override
	public void shutdown(RequestMonitor requestMonitor) {
        unregister();
		super.shutdown(requestMonitor);
	}
	
	/** 
	 * {@inheritDoc}
	 * 
	 * Starting with GDB 7.2, also provides information about which process each breakpoint applies to.
	 */
	@Override
	public void getBreakpoints(final IBreakpointsTargetDMContext context, final DataRequestMonitor<IBreakpointDMContext[]> drm)
	{
		if (bpThreadGroupInfoAvailable()) {
			// With GDB 7.6, we obtain the thread-groups to which a breakpoint applies
			// directly in the -break-list command, so we don't need to do any special processing.
			super.getBreakpoints(context, drm);
			return;
		}
		
		// Validate the context
		if (context == null) {
       		drm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_EXECUTION_CONTEXT, null));
       		drm.done();
			return;
		}

		// Select the breakpoints context map
		// If it doesn't exist then no breakpoint was ever inserted for this breakpoint space.
		// In that case, return an empty list.
		final Map<String, MIBreakpointDMData> breakpointContext = getBreakpointMap(context);
		if (breakpointContext == null) {
       		drm.setData(new IBreakpointDMContext[0]);
       		drm.done();
			return;
		}

		// Execute the command
		fConnection.queueCommand(fConnection.getCommandFactory().createMIBreakList(context),
			new DataRequestMonitor<MIBreakListInfo>(getExecutor(), drm) {
				@Override
				protected void handleSuccess() {
					final MIBreakpoint[] breakpoints = getData().getMIBreakpoints();
					
					// Also fetch the information about which breakpoint belongs to which
					// process.  We currently can only obtain this information from the CLI
					// command.  This information is needed for breakpoint filtering.
					// Bug 360735
					fConnection.queueCommand(fConnection.getCommandFactory().createCLIInfoBreak(context),
							new ImmediateDataRequestMonitor<CLIInfoBreakInfo>(drm) {
						@Override
						protected void handleSuccess() {
							Map<String, String[]> groupIdMap = getData().getBreakpointToGroupMap();

							// Refresh the breakpoints map and format the result
							breakpointContext.clear();
							IBreakpointDMContext[] result = new IBreakpointDMContext[breakpoints.length];
							for (int i = 0; i < breakpoints.length; i++) {
								MIBreakpointDMData breakpointData = new MIBreakpointDMData(breakpoints[i]);
								
								// Now fill in the thread-group information into the breakpoint data
								// It is ok to get null.  For example, pending breakpoints are not
								// associated to a thread-group; also, when debugging a single process,
								// the thread-group list is empty.
								String reference = breakpointData.getReference();
								String[] groupIds = groupIdMap.get(reference);
								breakpointData.setGroupIds(groupIds);
								
								result[i] = new MIBreakpointDMContext(GDBBreakpoints_7_2.this, new IDMContext[] { context }, reference);
								breakpointContext.put(reference, breakpointData);
							}
							drm.setData(result);
							drm.done();
						}
					});
				}
			});
	}
	
	private void setTracepointMode() {
		ILaunch launch = (ILaunch)getSession().getModelAdapter(ILaunch.class);
		String tpMode = IGDBLaunchConfigurationConstants.DEBUGGER_TRACEPOINT_MODE_DEFAULT;
		try {
			tpMode = launch.getLaunchConfiguration().getAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_TRACEPOINT_MODE,
					IGDBLaunchConfigurationConstants.DEBUGGER_TRACEPOINT_MODE_DEFAULT);
		} catch (CoreException e) {
		}

		if (tpMode.equals(IGDBLaunchConfigurationConstants.DEBUGGER_TRACEPOINT_FAST_ONLY)) {
			fTracepointMode = TracepointMode.FAST_ONLY;
		} else if (tpMode.equals(IGDBLaunchConfigurationConstants.DEBUGGER_TRACEPOINT_NORMAL_ONLY)) {
			fTracepointMode = TracepointMode.NORMAL_ONLY;
		} else if (tpMode.equals(IGDBLaunchConfigurationConstants.DEBUGGER_TRACEPOINT_FAST_THEN_NORMAL)) {
			fTracepointMode = TracepointMode.FAST_THEN_NORMAL;
		} else {
			assert false : "Invalid tracepoint mode: " + tpMode; //$NON-NLS-1$
			fTracepointMode = TracepointMode.NORMAL_ONLY;         
		}
	}

	protected void sendTracepointCommand(final IBreakpointsTargetDMContext context, final Map<String, Object> attributes, boolean isFastTracepoint, final DataRequestMonitor<IBreakpointDMContext> drm) {
		// Select the context breakpoints map
		final Map<String, MIBreakpointDMData> contextBreakpoints = getBreakpointMap(context);
		if (contextBreakpoints == null) {
			drm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_BREAKPOINT_CONTEXT, null));
			drm.done();
			return;
		}

		// Extract the relevant parameters (providing default values to avoid potential NPEs)
		final String location = formatLocation(attributes);
		if (location.equals(NULL_STRING)) {
			drm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_BREAKPOINT_CONTEXT, null));
			drm.done();
			return;
		}

		final Boolean enabled        = (Boolean) getProperty(attributes, MIBreakpoints.IS_ENABLED,        true);
		final String  condition      = (String)  getProperty(attributes, MIBreakpoints.CONDITION,         NULL_STRING);

		fConnection.queueCommand(
				fConnection.getCommandFactory().createMIBreakInsert(context, false, isFastTracepoint, condition, 0, location, "0", !enabled, true), //$NON-NLS-1$
				new DataRequestMonitor<MIBreakInsertInfo>(getExecutor(), drm) {
					@Override
					protected void handleSuccess() {
						// With MI, an invalid location won't generate an error
						if (getData().getMIBreakpoints().length == 0) {
							drm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, BREAKPOINT_INSERTION_FAILURE, null));
							drm.done();
							return;
						}

						// Create a breakpoint object and store it in the map
						final MIBreakpointDMData newBreakpoint = new MIBreakpointDMData(getData().getMIBreakpoints()[0]);
						String reference = newBreakpoint.getNumber();
						if (reference.isEmpty()) {
							drm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, BREAKPOINT_INSERTION_FAILURE, null));
							drm.done();
							return;
						}
						contextBreakpoints.put(reference, newBreakpoint);

						// Format the return value
						MIBreakpointDMContext dmc = new MIBreakpointDMContext(GDBBreakpoints_7_2.this, new IDMContext[] { context }, reference);
						drm.setData(dmc);

						// Flag the event
						getSession().dispatchEvent(new BreakpointAddedEvent(dmc), getProperties());

						// Tracepoints are created with no passcount (passcount are not 
						// the same thing as ignore-count, which is not supported by
						// tracepoints).  We have to set the passcount manually now.
						// Same for commands.
						Map<String,Object> delta = new HashMap<String,Object>();
						delta.put(MIBreakpoints.PASS_COUNT, getProperty(attributes, MIBreakpoints.PASS_COUNT, 0));
						delta.put(MIBreakpoints.COMMANDS, getProperty(attributes, MIBreakpoints.COMMANDS, "")); //$NON-NLS-1$
						modifyBreakpoint(dmc, delta, drm, false);
					}

					@Override
					protected void handleError() {
						drm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, BREAKPOINT_INSERTION_FAILURE, getStatus().getException()));
						drm.done();
					}
				});
	}
	/**
	 * Add a tracepoint using MI.  We have three settings:
	 *   1- set only a fast tracepoint but if it fails, set a normal tracepoint
	 *   2- only set a fast tracepoint even if it fails
	 *   3- only set a normal tracepoint even if a fast tracepoint could have been used
	 */
	@Override
	protected void addTracepoint(final IBreakpointsTargetDMContext context, final Map<String, Object> attributes, final DataRequestMonitor<IBreakpointDMContext> drm) {
		// Unless we should only set normal tracepoints, we try to set a fast tracepoint.
		boolean isFastTracepoint = fTracepointMode != TracepointMode.NORMAL_ONLY;

		sendTracepointCommand(context, attributes, isFastTracepoint, new ImmediateDataRequestMonitor<IBreakpointDMContext>(drm) {
			@Override
			protected void handleSuccess() {
				// Tracepoint was set successfully.
				drm.setData(getData());
				drm.done();
			}
			@Override
			protected void handleError() {
				// Tracepoint failed to be set.
				if (fTracepointMode == TracepointMode.FAST_THEN_NORMAL) {
					// In this case, we failed to set a fast tracepoint, but we should try to set a normal one.
					sendTracepointCommand(context, attributes, false, drm);
				} else {
					// We either failed to set a fast tracepoint and we should not try to set a normal one,
					// or we failed to set a normal one.  Either way, we are done.
					drm.setStatus(getStatus());
					drm.done();
				}
			}
		});
	}
	
	/**
	 * Does the MI command -break-list provide information
	 * about which thread-group a breakpoint applies to?
	 * The use of this method allows us to avoid duplicating code.
	 * See Bug 402217
	 * 
	 * @return true if the information is available (GDB >= 7.6),
	 *         false otherwise.
	 * @since 4.2
	 */
	protected boolean bpThreadGroupInfoAvailable() {
		return false;
	}
}

Back to the top