cdt/cdt_6_0_x/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/MIBreakpoints.java
Overhauled BreakpointsMediator to support both EDC and GDB properly. See Eclipse bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=292468
/*******************************************************************************
* Copyright (c) 2007, 2008 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.mi.service;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import org.eclipse.cdt.debug.core.CDebugCorePlugin;
import org.eclipse.cdt.debug.core.model.ICBreakpoint;
import org.eclipse.cdt.dsf.concurrent.CountingRequestMonitor;
import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
import org.eclipse.cdt.dsf.concurrent.Immutable;
import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
import org.eclipse.cdt.dsf.datamodel.AbstractDMContext;
import org.eclipse.cdt.dsf.datamodel.AbstractDMEvent;
import org.eclipse.cdt.dsf.datamodel.DMContexts;
import org.eclipse.cdt.dsf.datamodel.IDMContext;
import org.eclipse.cdt.dsf.debug.service.IBreakpoints;
import org.eclipse.cdt.dsf.debug.service.command.ICommandControl;
import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlShutdownDMEvent;
import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin;
import org.eclipse.cdt.dsf.mi.service.MIRunControl.SuspendedEvent;
import org.eclipse.cdt.dsf.mi.service.breakpoint.actions.BreakpointActionAdapter;
import org.eclipse.cdt.dsf.mi.service.command.commands.MIBreakAfter;
import org.eclipse.cdt.dsf.mi.service.command.commands.MIBreakCondition;
import org.eclipse.cdt.dsf.mi.service.command.commands.MIBreakDelete;
import org.eclipse.cdt.dsf.mi.service.command.commands.MIBreakDisable;
import org.eclipse.cdt.dsf.mi.service.command.commands.MIBreakEnable;
import org.eclipse.cdt.dsf.mi.service.command.commands.MIBreakInsert;
import org.eclipse.cdt.dsf.mi.service.command.commands.MIBreakList;
import org.eclipse.cdt.dsf.mi.service.command.commands.MIBreakWatch;
import org.eclipse.cdt.dsf.mi.service.command.events.MIBreakpointHitEvent;
import org.eclipse.cdt.dsf.mi.service.command.events.MIWatchpointScopeEvent;
import org.eclipse.cdt.dsf.mi.service.command.events.MIWatchpointTriggerEvent;
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.mi.service.command.output.MIInfo;
import org.eclipse.cdt.dsf.service.AbstractDsfService;
import org.eclipse.cdt.dsf.service.DsfServiceEventHandler;
import org.eclipse.cdt.dsf.service.DsfSession;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.osgi.framework.BundleContext;
/**
* Initial breakpoint service implementation.
* Implements the IBreakpoints interface.
*/
public class MIBreakpoints extends AbstractDsfService implements IBreakpoints
{
/**
* Breakpoint attributes markers used in the map parameters of insert/updateBreakpoint().
* All are optional with the possible exception of TYPE. It is the responsibility of the
* service to ensure that the set of attributes provided is sufficient to create/update
* a valid breakpoint on the back-end.
*/
public static final String PREFIX = "org.eclipse.cdt.dsf.debug.breakpoint"; //$NON-NLS-1$
// General markers
public static final String BREAKPOINT_TYPE = PREFIX + ".type"; //$NON-NLS-1$
public static final String BREAKPOINT = "breakpoint"; //$NON-NLS-1$
public static final String WATCHPOINT = "watchpoint"; //$NON-NLS-1$
public static final String CATCHPOINT = "catchpoint"; //$NON-NLS-1$
// Basic set of breakpoint attribute markers
public static final String FILE_NAME = PREFIX + ".fileName"; //$NON-NLS-1$
public static final String LINE_NUMBER = PREFIX + ".lineNumber"; //$NON-NLS-1$
public static final String FUNCTION = PREFIX + ".function"; //$NON-NLS-1$
public static final String ADDRESS = PREFIX + ".address"; //$NON-NLS-1$
public static final String CONDITION = PREFIX + ".condition"; //$NON-NLS-1$
public static final String IGNORE_COUNT = PREFIX + ".ignoreCount"; //$NON-NLS-1$
public static final String IS_ENABLED = PREFIX + ".isEnabled"; //$NON-NLS-1$
// Basic set of watchpoint attribute markers
public static final String EXPRESSION = PREFIX + ".expression"; //$NON-NLS-1$
public static final String READ = PREFIX + ".read"; //$NON-NLS-1$
public static final String WRITE = PREFIX + ".write"; //$NON-NLS-1$
// Services
ICommandControl fConnection;
// Service breakpoints tracking
// The breakpoints are stored per context and keyed on the back-end breakpoint reference
private Map<IBreakpointsTargetDMContext, Map<Integer, MIBreakpointDMData>> fBreakpoints =
new HashMap<IBreakpointsTargetDMContext, Map<Integer, MIBreakpointDMData>>();
// Error messages
final String NULL_STRING = ""; //$NON-NLS-1$
final String UNKNOWN_EXECUTION_CONTEXT = "Unknown execution context"; //$NON-NLS-1$
final String UNKNOWN_BREAKPOINT_CONTEXT = "Unknown breakpoint context"; //$NON-NLS-1$
final String UNKNOWN_BREAKPOINT_TYPE = "Unknown breakpoint type"; //$NON-NLS-1$
final String UNKNOWN_BREAKPOINT = "Unknown breakpoint"; //$NON-NLS-1$
final String BREAKPOINT_INSERTION_FAILURE = "Breakpoint insertion failure"; //$NON-NLS-1$
final String WATCHPOINT_INSERTION_FAILURE = "Watchpoint insertion failure"; //$NON-NLS-1$
final String INVALID_CONDITION = "Invalid condition"; //$NON-NLS-1$
///////////////////////////////////////////////////////////////////////////
// Breakpoint Events
///////////////////////////////////////////////////////////////////////////
public class BreakpointsChangedEvent extends AbstractDMEvent<IBreakpointsTargetDMContext> implements IBreakpointsChangedEvent {
private IBreakpointDMContext[] fEventBreakpoints;
public BreakpointsChangedEvent(IBreakpointDMContext bp) {
super(DMContexts.getAncestorOfType(bp, IBreakpointsTargetDMContext.class));
fEventBreakpoints = new IBreakpointDMContext[] { bp };
}
public IBreakpointDMContext[] getBreakpoints() {
return fEventBreakpoints;
}
}
public class BreakpointAddedEvent extends BreakpointsChangedEvent implements IBreakpointsAddedEvent {
public BreakpointAddedEvent(IBreakpointDMContext context) {
super(context);
}
}
public class BreakpointUpdatedEvent extends BreakpointsChangedEvent implements IBreakpointsUpdatedEvent {
public BreakpointUpdatedEvent(IBreakpointDMContext context) {
super(context);
}
}
public class BreakpointRemovedEvent extends BreakpointsChangedEvent implements IBreakpointsRemovedEvent {
public BreakpointRemovedEvent(IBreakpointDMContext context) {
super(context);
}
}
///////////////////////////////////////////////////////////////////////////
// IBreakpointDMContext
// Used to hold the back-end breakpoint references. The reference can then
// be used to get the actual DsfMIBreakpoint.
///////////////////////////////////////////////////////////////////////////
@Immutable
public static final class MIBreakpointDMContext extends AbstractDMContext implements IBreakpointDMContext {
// The breakpoint reference
private final Integer fReference;
/**
* @param service the Breakpoint service
* @param parents the parent contexts
* @param reference the DsfMIBreakpoint reference
*/
public MIBreakpointDMContext(MIBreakpoints service, IDMContext[] parents, int reference) {
super(service.getSession().getId(), parents);
fReference = reference;
}
/* (non-Javadoc)
* @see org.eclipse.cdt.dsf.debug.service.IBreakpoints.IDsfBreakpointDMContext#getReference()
*/
public int getReference() {
return fReference;
}
/* (non-Javadoc)
* @see org.eclipse.cdt.dsf.datamodel.AbstractDMContext#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
return baseEquals(obj) && (fReference == ((MIBreakpointDMContext) obj).fReference);
}
/* (non-Javadoc)
* @see org.eclipse.cdt.dsf.datamodel.AbstractDMContext#hashCode()
*/
@Override
public int hashCode() {
return baseHashCode() + fReference.hashCode();
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return baseToString() + ".reference(" + fReference + ")"; //$NON-NLS-1$//$NON-NLS-2$*/
}
}
///////////////////////////////////////////////////////////////////////////
// AbstractDsfService
///////////////////////////////////////////////////////////////////////////
/**
* The service constructor
*
* @param session The debugging session
*/
public MIBreakpoints(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 RequestMonitor(getExecutor(), rm) {
@Override
protected void handleSuccess() {
doInitialize(rm);
}
});
}
/*
* Asynchronous service initialization
*/
private void doInitialize(final RequestMonitor rm) {
// Get the services references
fConnection = getServicesTracker().getService(ICommandControl.class);
// Register for the useful events
getSession().addServiceEventListener(this, null);
// Register this service
register(new String[] { IBreakpoints.class.getName(), MIBreakpoints.class.getName() },
new Hashtable<String, String>());
rm.done();
}
/* (non-Javadoc)
* @see org.eclipse.cdt.dsf.service.AbstractDsfService#shutdown(org.eclipse.cdt.dsf.concurrent.RequestMonitor)
*/
@Override
public void shutdown(final RequestMonitor rm) {
unregister();
getSession().removeServiceEventListener(this);
rm.done();
}
/* (non-Javadoc)
* @see org.eclipse.cdt.dsf.service.AbstractDsfService#getBundleContext()
*/
@Override
protected BundleContext getBundleContext() {
return GdbPlugin.getBundleContext();
}
///////////////////////////////////////////////////////////////////////////
// IServiceEventListener
///////////////////////////////////////////////////////////////////////////
/**
* This method is left for API compatibility only.
* @nooverride This method is not intended to be re-implemented or extended by clients.
* @noreference This method is not intended to be referenced by clients.
*/
@DsfServiceEventHandler
public void eventDispatched(MIWatchpointScopeEvent e) {
// When a watchpoint goes out of scope, it is automatically removed from
// the back-end. To keep our internal state synchronized, we have to
// remove it from our breakpoints map.
IBreakpointsTargetDMContext bpContext = DMContexts.getAncestorOfType(e.getDMContext(), IBreakpointsTargetDMContext.class);
if (bpContext != null) {
Map<Integer, MIBreakpointDMData> contextBps = fBreakpoints.get(bpContext);
if (contextBps != null) {
contextBps.remove(e.getNumber());
}
}
}
/**
* @since 1.1
* @nooverride This method is not intended to be re-implemented or extended by clients.
* @noreference This method is not intended to be referenced by clients.
*/
@DsfServiceEventHandler
public void eventDispatched(ICommandControlShutdownDMEvent e) {
}
@DsfServiceEventHandler
public void eventDispatched(SuspendedEvent e) {
if (e.getMIEvent() instanceof MIBreakpointHitEvent) {
MIBreakpointHitEvent evt = (MIBreakpointHitEvent) e.getMIEvent();
performBreakpointAction(evt.getDMContext(), evt.getNumber());
return;
}
if (e.getMIEvent() instanceof MIWatchpointTriggerEvent) {
MIWatchpointTriggerEvent evt = (MIWatchpointTriggerEvent) e.getMIEvent();
performBreakpointAction(evt.getDMContext(), evt.getNumber());
return;
}
}
private void performBreakpointAction(final IDMContext context, int number) {
// Identify the platform breakpoint
final ICBreakpoint breakpoint = findPlatformBreakpoint(context, number);
// Perform the actions asynchronously (otherwise we can have a deadlock...)
new Job("Breakpoint action") { //$NON-NLS-1$
{ setSystem(true); }
@Override
protected IStatus run(IProgressMonitor monitor) {
CDebugCorePlugin.getDefault().getBreakpointActionManager().executeActions(breakpoint, new BreakpointActionAdapter(getExecutor(), getServicesTracker(), context));
return Status.OK_STATUS;
};
}.schedule();
}
// Helper function to locate the platform breakpoint corresponding
// to the target breakpoint/watchpoint that was just hit
// FIXME: (Bug228703) Need a way to identify the correct context where the BP was hit
private ICBreakpoint findPlatformBreakpoint(IDMContext context, int targetBreakpointID) {
// Hmm, the service has no tracking of MIBreakpointDMContext. So this workaround.
IBreakpointsTargetDMContext btDMC = DMContexts.getAncestorOfType(context, IBreakpointsTargetDMContext.class);
assert btDMC != null;
IBreakpointDMContext dmc = new MIBreakpointDMContext(MIBreakpoints.this, new IDMContext[] { btDMC }, targetBreakpointID);
MIBreakpointsManager bpMediator = getServicesTracker().getService(MIBreakpointsManager.class);
ICBreakpoint cdtBP = (ICBreakpoint)bpMediator.getPlatformBreakpoint(btDMC, dmc);
return cdtBP;
}
///////////////////////////////////////////////////////////////////////////
// IBreakpoints interface
///////////////////////////////////////////////////////////////////////////
//-------------------------------------------------------------------------
// getBreakpoints
//-------------------------------------------------------------------------
/* (non-Javadoc)
* @see org.eclipse.cdt.dsf.debug.service.IBreakpoints#getBreakpoints(org.eclipse.cdt.dsf.debug.service.IBreakpoints.IBreakpointsTargetDMContext, org.eclipse.cdt.dsf.concurrent.DataRequestMonitor)
*/
public void getBreakpoints(final IBreakpointsTargetDMContext context, final DataRequestMonitor<IBreakpointDMContext[]> drm)
{
// 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<Integer, MIBreakpointDMData> breakpointContext = fBreakpoints.get(context);
if (breakpointContext == null) {
drm.setData(new IBreakpointDMContext[0]);
drm.done();
return;
}
// Execute the command
fConnection.queueCommand(new MIBreakList(context),
new DataRequestMonitor<MIBreakListInfo>(getExecutor(), drm) {
@Override
protected void handleSuccess() {
// Refresh the breakpoints map and format the result
breakpointContext.clear();
MIBreakpoint[] breakpoints = getData().getMIBreakpoints();
IBreakpointDMContext[] result = new IBreakpointDMContext[breakpoints.length];
for (int i = 0; i < breakpoints.length; i++) {
MIBreakpointDMData breakpoint = new MIBreakpointDMData(breakpoints[i]);
int reference = breakpoint.getReference();
result[i] = new MIBreakpointDMContext(MIBreakpoints.this, new IDMContext[] { context }, reference);
breakpointContext.put(reference, breakpoint);
}
drm.setData(result);
drm.done();
}
});
}
//-------------------------------------------------------------------------
// getBreakpointDMData
//-------------------------------------------------------------------------
/* (non-Javadoc)
* @see org.eclipse.cdt.dsf.debug.service.IBreakpoints#getBreakpointDMData(org.eclipse.cdt.dsf.debug.service.IBreakpoints.IDsfBreakpointDMContext, org.eclipse.cdt.dsf.concurrent.DataRequestMonitor)
*/
public void getBreakpointDMData(IBreakpointDMContext dmc, DataRequestMonitor<IBreakpointDMData> drm)
{
// Validate the breakpoint context
if (dmc == null) {
drm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_BREAKPOINT_CONTEXT, null));
drm.done();
return;
}
// Validate the breakpoint type
MIBreakpointDMContext breakpoint;
if (dmc instanceof MIBreakpointDMContext) {
breakpoint = (MIBreakpointDMContext) dmc;
}
else {
drm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_BREAKPOINT_TYPE, null));
drm.done();
return;
}
// Validate the target context
IBreakpointsTargetDMContext context = DMContexts.getAncestorOfType(breakpoint, IBreakpointsTargetDMContext.class);
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
Map<Integer, MIBreakpointDMData> contextBreakpoints = fBreakpoints.get(context);
if (contextBreakpoints == null) {
drm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_BREAKPOINT, null));
drm.done();
return;
}
// No need to go to the back-end for this one
IBreakpointDMData breakpointCopy = new MIBreakpointDMData(contextBreakpoints.get(breakpoint.getReference()));
drm.setData(breakpointCopy);
drm.done();
}
//-------------------------------------------------------------------------
// insertBreakpoint
//-------------------------------------------------------------------------
/* (non-Javadoc)
* @see org.eclipse.cdt.dsf.debug.service.IBreakpoints#insertBreakpoint(org.eclipse.cdt.dsf.debug.service.IBreakpoints.IBreakpointsTargetDMContext, java.util.Map, org.eclipse.cdt.dsf.concurrent.DataRequestMonitor)
*/
public void insertBreakpoint(IBreakpointsTargetDMContext context, Map<String, Object> attributes, DataRequestMonitor<IBreakpointDMContext> drm) {
// 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, create it.
Map<Integer, MIBreakpointDMData> breakpointContext = fBreakpoints.get(context);
if (breakpointContext == null) {
breakpointContext = new HashMap<Integer, MIBreakpointDMData>();
fBreakpoints.put(context, breakpointContext);
}
// Validate the breakpoint type
String type = (String) attributes.get(BREAKPOINT_TYPE);
if (type == null) {
drm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_BREAKPOINT_TYPE, null));
drm.done();
return;
}
// And go...
if (type.equals(BREAKPOINT)) {
addBreakpoint(context, attributes, drm);
}
else if (type.equals(WATCHPOINT)) {
addWatchpoint(context, attributes, drm);
}
else {
drm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_BREAKPOINT_TYPE, null));
drm.done();
}
}
/**
* @param map
* @param key
* @param defaultValue
* @return
*/
public Object getProperty(Map<String, Object> map, String key, Object defaultValue) {
return (map.containsKey(key) && (map.get(key) != null)) ? map.get(key) : defaultValue;
}
/**
* @param attributes
* @return
*/
private String formatLocation(Map<String, Object> attributes) {
// Unlikely default location
String location = (String) getProperty(attributes, ADDRESS, NULL_STRING);
// Get the relevant parameters
String fileName = (String) getProperty(attributes, FILE_NAME, NULL_STRING);
Integer lineNumber = (Integer) getProperty(attributes, LINE_NUMBER, -1);
String function = (String) getProperty(attributes, FUNCTION, NULL_STRING);
// Fix for Bug264721
if (fileName.contains(" ")) { //$NON-NLS-1$
fileName = "\"" + fileName + "\""; //$NON-NLS-1$//$NON-NLS-2$
}
if (!fileName.equals(NULL_STRING)) {
if (lineNumber != -1) {
location = fileName + ":" + lineNumber; //$NON-NLS-1$
} else {
location = fileName + ":" + function; //$NON-NLS-1$
}
} else if (!function.equals(NULL_STRING)) {
// function location without source
location = function;
} else if (location.length() > 0) {
// address location
if (Character.isDigit(location.charAt(0))) {
// numeric address needs '*' prefix
location = '*' + location;
}
}
return location;
}
/**
* Add a breakpoint of type BREAKPOINT
*
* @param context
* @param breakpoint
* @param drm
*/
private void addBreakpoint(final IBreakpointsTargetDMContext context, final Map<String, Object> attributes, final DataRequestMonitor<IBreakpointDMContext> drm)
{
// Select the context breakpoints map
final Map<Integer, MIBreakpointDMData> contextBreakpoints = fBreakpoints.get(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)
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;
}
Boolean isTemporary = (Boolean) getProperty(attributes, MIBreakpointDMData.IS_TEMPORARY, false);
Boolean isHardware = (Boolean) getProperty(attributes, MIBreakpointDMData.IS_HARDWARE, false);
final String condition = (String) getProperty(attributes, CONDITION, NULL_STRING);
Integer ignoreCount = (Integer) getProperty(attributes, IGNORE_COUNT, 0 );
String threadId = (String) getProperty(attributes, MIBreakpointDMData.THREAD_ID, "0"); //$NON-NLS-1$
int tid = Integer.parseInt(threadId);
// The DataRequestMonitor for the add request
DataRequestMonitor<MIBreakInsertInfo> addBreakpointDRM =
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]);
int reference = newBreakpoint.getNumber();
if (reference == -1) {
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(MIBreakpoints.this, new IDMContext[] { context }, reference);
drm.setData(dmc);
// Flag the event
getSession().dispatchEvent(new BreakpointAddedEvent(dmc), getProperties());
// By default the breakpoint is enabled at creation
// If it wasn't supposed to be, then disable it right away
Map<String,Object> delta = new HashMap<String,Object>();
delta.put(IS_ENABLED, getProperty(attributes, IS_ENABLED, true));
modifyBreakpoint(dmc, delta, drm, false);
}
@Override
protected void handleError() {
drm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, BREAKPOINT_INSERTION_FAILURE, null));
drm.done();
}
};
// Execute the command
fConnection.queueCommand(
new MIBreakInsert(context, isTemporary, isHardware, condition, ignoreCount, location, tid), addBreakpointDRM);
}
/**
* Add a breakpoint of type WATCHPOINT
*
* @param context
* @param watchpoint
* @param drm
*/
private void addWatchpoint(final IBreakpointsTargetDMContext context, final Map<String, Object> attributes, final DataRequestMonitor<IBreakpointDMContext> drm)
{
// Pick the context breakpoints map
final Map<Integer, MIBreakpointDMData> contextBreakpoints = fBreakpoints.get(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)
String expression = (String) getProperty(attributes, EXPRESSION, NULL_STRING);
boolean isRead = (Boolean) getProperty(attributes, READ, false);
boolean isWrite = (Boolean) getProperty(attributes, WRITE, false);
// The DataRequestMonitor for the add request
DataRequestMonitor<MIBreakInsertInfo> addWatchpointDRM =
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, WATCHPOINT_INSERTION_FAILURE, null));
drm.done();
return;
}
// Create a breakpoint object and store it in the map
final MIBreakpointDMData newBreakpoint = new MIBreakpointDMData(getData().getMIBreakpoints()[0]);
int reference = newBreakpoint.getNumber();
if (reference == -1) {
drm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, WATCHPOINT_INSERTION_FAILURE, null));
drm.done();
return;
}
contextBreakpoints.put(reference, newBreakpoint);
// Format the return value
IBreakpointDMContext dmc = new MIBreakpointDMContext(MIBreakpoints.this, new IDMContext[] { context }, reference);
drm.setData(dmc);
// Flag the event
getSession().dispatchEvent(new BreakpointAddedEvent(dmc), getProperties());
// Condition, ignore count and state can not be specified at watchpoint creation time.
// Therefore, we have to update the watchpoint if any of these is present
Map<String,Object> delta = new HashMap<String,Object>();
delta.put(CONDITION, getProperty(attributes, CONDITION, NULL_STRING));
delta.put(IGNORE_COUNT, getProperty(attributes, IGNORE_COUNT, 0 ));
delta.put(IS_ENABLED, getProperty(attributes, IS_ENABLED, true));
modifyBreakpoint(dmc, delta, drm, false);
}
@Override
protected void handleError() {
drm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, WATCHPOINT_INSERTION_FAILURE, null));
drm.done();
}
};
// Execute the command
fConnection.queueCommand(new MIBreakWatch(context, isRead, isWrite, expression), addWatchpointDRM);
}
//-------------------------------------------------------------------------
// removeBreakpoint
//-------------------------------------------------------------------------
/* (non-Javadoc)
* @see org.eclipse.cdt.dsf.debug.service.IBreakpoints#removeBreakpoint(org.eclipse.cdt.dsf.debug.service.IBreakpoints.IBreakpointDMContext, org.eclipse.cdt.dsf.concurrent.RequestMonitor)
*/
public void removeBreakpoint(final IBreakpointDMContext dmc, final RequestMonitor rm) {
// Validate the breakpoint context
if (dmc == null) {
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_BREAKPOINT_CONTEXT, null));
rm.done();
return;
}
// Validate the breakpoint type
MIBreakpointDMContext breakpointCtx;
if (dmc instanceof MIBreakpointDMContext) {
breakpointCtx = (MIBreakpointDMContext) dmc;
}
else {
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_BREAKPOINT_TYPE, null));
rm.done();
return;
}
// Validate the target context
IBreakpointsTargetDMContext context = DMContexts.getAncestorOfType(dmc, IBreakpointsTargetDMContext.class);
if (context == null) {
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_EXECUTION_CONTEXT, null));
rm.done();
return;
}
// Pick the context breakpoints map
final Map<Integer,MIBreakpointDMData> contextBreakpoints = fBreakpoints.get(context);
if (contextBreakpoints == null) {
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_BREAKPOINT, null));
rm.done();
return;
}
// Validate the breakpoint
final int reference = breakpointCtx.getReference();
MIBreakpointDMData breakpoint = contextBreakpoints.get(reference);
if (breakpoint == null) {
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_BREAKPOINT, null));
rm.done();
return;
}
// Queue the command
fConnection.queueCommand(
new MIBreakDelete(context, new int[] { reference }),
new DataRequestMonitor<MIInfo>(getExecutor(), rm) {
@Override
protected void handleCompleted() {
if (isSuccess()) {
getSession().dispatchEvent(new BreakpointRemovedEvent(dmc), getProperties());
contextBreakpoints.remove(reference);
}
rm.done();
}
});
}
// -------------------------------------------------------------------------
// updateBreakpoint
//-------------------------------------------------------------------------
/* (non-Javadoc)
* @see org.eclipse.cdt.dsf.debug.service.IBreakpoints#updateBreakpoint(org.eclipse.cdt.dsf.debug.service.IBreakpoints.IBreakpointDMContext, java.util.Map, org.eclipse.cdt.dsf.concurrent.RequestMonitor)
*/
public void updateBreakpoint(IBreakpointDMContext dmc, Map<String, Object> properties, RequestMonitor rm)
{
// Validate the breakpoint context
if (dmc == null) {
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_BREAKPOINT_CONTEXT, null));
rm.done();
return;
}
// Validate the breakpoint type
MIBreakpointDMContext breakpointCtx;
if (dmc instanceof MIBreakpointDMContext) {
breakpointCtx = (MIBreakpointDMContext) dmc;
}
else {
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_BREAKPOINT_TYPE, null));
rm.done();
return;
}
// Validate the context
IBreakpointsTargetDMContext context = DMContexts.getAncestorOfType(dmc, IBreakpointsTargetDMContext.class);
if (context == null) {
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_EXECUTION_CONTEXT, null));
rm.done();
return;
}
// Pick the context breakpoints map
final Map<Integer, MIBreakpointDMData> contextBreakpoints = fBreakpoints.get(context);
if (contextBreakpoints == null) {
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_BREAKPOINT, null));
rm.done();
return;
}
// Validate the breakpoint
final int reference = breakpointCtx.getReference();
MIBreakpointDMData breakpoint = contextBreakpoints.get(reference);
if (breakpoint == null) {
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_BREAKPOINT, null));
rm.done();
return;
}
modifyBreakpoint(dmc, properties, rm, true);
}
/**
* @param dmc
* @param properties
* @param rm
* @param generateUpdateEvent
*/
private void modifyBreakpoint(final IBreakpointDMContext dmc, Map<String, Object> attributes, final RequestMonitor rm, final boolean generateUpdateEvent)
{
// Use a working copy of the attributes since we are going to tamper happily with them
Map<String, Object> properties = new HashMap<String, Object>(attributes);
// Retrieve the breakpoint parameters
// At this point, we know their are OK so there is no need to re-validate
MIBreakpointDMContext breakpointCtx = (MIBreakpointDMContext) dmc;
final IBreakpointsTargetDMContext context = DMContexts.getAncestorOfType(dmc, IBreakpointsTargetDMContext.class);
final Map<Integer, MIBreakpointDMData> contextBreakpoints = fBreakpoints.get(context);
final int reference = breakpointCtx.getReference();
MIBreakpointDMData breakpoint = contextBreakpoints.get(reference);
// Track the number of change requests
int numberOfChanges = 0;
final CountingRequestMonitor countingRm = new CountingRequestMonitor(getExecutor(), rm) {
@Override
protected void handleSuccess() {
if (generateUpdateEvent)
getSession().dispatchEvent(new BreakpointUpdatedEvent(dmc), getProperties());
rm.done();
}
};
// Determine if the breakpoint condition changed
String conditionAttribute = CONDITION;
if (properties.containsKey(conditionAttribute)) {
final String oldValue = breakpoint.getCondition();
String newValue = (String) properties.get(conditionAttribute);
if (newValue == null) newValue = NULL_STRING;
if (!oldValue.equals(newValue)) {
changeCondition(context, reference, newValue, new RequestMonitor(getExecutor(), countingRm){
@Override
protected void handleError() {
// Failed to change the condition, restore the old condition.
// See comment in changeCondition() for more.
MIBreakpointsManager bpMediator = getServicesTracker().getService(MIBreakpointsManager.class);
final ICBreakpoint cdtBP = (ICBreakpoint)bpMediator.getPlatformBreakpoint(context, dmc);
rollbackCondition(cdtBP, oldValue);
countingRm.done();
}});
numberOfChanges++;
}
properties.remove(conditionAttribute);
}
// Determine if the breakpoint ignore count changed
String ignoreCountAttribute = IGNORE_COUNT;
if (properties.containsKey(ignoreCountAttribute)) {
Integer oldValue = breakpoint.getIgnoreCount();
Integer newValue = (Integer) properties.get(ignoreCountAttribute);
if (newValue == null) newValue = 0;
if (!oldValue.equals(newValue)) {
changeIgnoreCount(context, reference, newValue, countingRm);
numberOfChanges++;
}
properties.remove(ignoreCountAttribute);
}
// Determine if the breakpoint state changed
String enableAttribute = IS_ENABLED;
if (properties.containsKey(enableAttribute)) {
Boolean oldValue = breakpoint.isEnabled();
Boolean newValue = (Boolean) properties.get(enableAttribute);
if (newValue == null) newValue = false;
if (!oldValue.equals(newValue)) {
numberOfChanges++;
if (newValue)
enableBreakpoint(context, reference, countingRm);
else
disableBreakpoint(context, reference, countingRm);
}
properties.remove(enableAttribute);
}
// Set the number of completions required
countingRm.setDoneCount(numberOfChanges);
}
private void rollbackCondition(final ICBreakpoint cdtBP, final String oldValue) {
if (cdtBP == null)
return;
new Job("rollback breakpont condition") { //$NON-NLS-1$
{ setSystem(true); }
@Override
protected IStatus run(IProgressMonitor monitor) {
try {
if (oldValue != null)
cdtBP.setCondition(oldValue);
else
cdtBP.setCondition(NULL_STRING);
} catch (CoreException e) {
// ignore
}
return Status.OK_STATUS;
}}.schedule();
}
/**
* Update the breakpoint condition
*
* @param context
* @param dmc
* @param condition
* @param rm
*/
private void changeCondition(final IBreakpointsTargetDMContext context,
final int reference, final String condition, final RequestMonitor rm)
{
// Pick the context breakpoints map
final Map<Integer, MIBreakpointDMData> contextBreakpoints = fBreakpoints.get(context);
if (contextBreakpoints == null) {
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_BREAKPOINT_CONTEXT, null));
rm.done();
return;
}
// Queue the command
fConnection.queueCommand(
new MIBreakCondition(context, reference, condition),
new DataRequestMonitor<MIInfo>(getExecutor(), rm) {
@Override
protected void handleSuccess() {
MIBreakpointDMData breakpoint = contextBreakpoints.get(reference);
if (breakpoint == null) {
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_BREAKPOINT, null));
rm.done();
return;
}
breakpoint.setCondition(condition);
rm.done();
}
// In case of error (new condition could not be installed for whatever reason),
// GDB "offers" different behaviours depending on its version: it can either keep
// the original condition (the right thing to do) or keep the invalid condition.
// Our sole option is to remove the condition in case of error and rely on the
// upper layer to re-install the right condition.
@Override
protected void handleError() {
MIBreakpointDMData breakpoint = contextBreakpoints.get(reference);
if (breakpoint == null) {
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_BREAKPOINT, null));
rm.done();
return;
}
// Remove invalid condition from the back-end breakpoint
breakpoint.setCondition(NULL_STRING);
fConnection.queueCommand(
new MIBreakCondition(context, reference, NULL_STRING),
new DataRequestMonitor<MIInfo>(getExecutor(), rm) {
@Override
// The report the initial problem
protected void handleCompleted() {
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, INVALID_CONDITION, null));
rm.done();
}
});
}
});
}
/**
* Update the breakpoint ignoreCount
*
* @param context
* @param reference
* @param ignoreCount
* @param rm
*/
private void changeIgnoreCount(IBreakpointsTargetDMContext context,
final int reference, final int ignoreCount, final RequestMonitor rm)
{
// Pick the context breakpoints map
final Map<Integer, MIBreakpointDMData> contextBreakpoints = fBreakpoints.get(context);
if (contextBreakpoints == null) {
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_BREAKPOINT_CONTEXT, null));
rm.done();
return;
}
// Queue the command
fConnection.queueCommand(
new MIBreakAfter(context, reference, ignoreCount),
new DataRequestMonitor<MIInfo>(getExecutor(), rm) {
@Override
protected void handleSuccess() {
MIBreakpointDMData breakpoint = contextBreakpoints.get(reference);
if (breakpoint == null) {
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_BREAKPOINT, null));
rm.done();
return;
}
breakpoint.setIgnoreCount(ignoreCount);
rm.done();
}
});
}
/**
* Enable the breakpoint
*
* @param context
* @param reference
* @param rm
*/
private void enableBreakpoint(IBreakpointsTargetDMContext context,
final int reference, final RequestMonitor rm)
{
// Pick the context breakpoints map
final Map<Integer, MIBreakpointDMData> contextBreakpoints = fBreakpoints.get(context);
if (contextBreakpoints == null) {
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_BREAKPOINT_CONTEXT, null));
rm.done();
return;
}
// Queue the command
fConnection.queueCommand(
new MIBreakEnable(context, new int[] { reference }),
new DataRequestMonitor<MIInfo>(getExecutor(), rm) {
@Override
protected void handleSuccess() {
MIBreakpointDMData breakpoint = contextBreakpoints.get(reference);
if (breakpoint == null) {
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_BREAKPOINT, null));
rm.done();
return;
}
breakpoint.setEnabled(true);
rm.done();
}
});
}
/**
* Disable the breakpoint
*
* @param context
* @param dmc
* @param rm
*/
private void disableBreakpoint(IBreakpointsTargetDMContext context,
final int reference, final RequestMonitor rm)
{
// Pick the context breakpoints map
final Map<Integer, MIBreakpointDMData> contextBreakpoints = fBreakpoints.get(context);
if (contextBreakpoints == null) {
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_BREAKPOINT_CONTEXT, null));
rm.done();
return;
}
// Queue the command
fConnection.queueCommand(
new MIBreakDisable(context, new int[] { reference }),
new DataRequestMonitor<MIInfo>(getExecutor(), rm) {
@Override
protected void handleSuccess() {
MIBreakpointDMData breakpoint = contextBreakpoints.get(reference);
if (breakpoint == null) {
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_BREAKPOINT, null));
rm.done();
return;
}
breakpoint.setEnabled(false);
rm.done();
}
});
}
}