mirror of
https://github.com/eclipse-cdt/cdt
synced 2025-04-29 19:45:01 +02:00
[248587] Support "Event' Breakpoints
This commit is contained in:
parent
cdb45fd02a
commit
57c6ba5fec
21 changed files with 2091 additions and 49 deletions
|
@ -29,15 +29,15 @@ import org.eclipse.cdt.dsf.datamodel.DMContexts;
|
|||
import org.eclipse.cdt.dsf.datamodel.IDMContext;
|
||||
import org.eclipse.cdt.dsf.datamodel.IDMEvent;
|
||||
import org.eclipse.cdt.dsf.debug.service.IBreakpoints;
|
||||
import org.eclipse.cdt.dsf.debug.service.ICachingService;
|
||||
import org.eclipse.cdt.dsf.debug.service.IProcesses;
|
||||
import org.eclipse.cdt.dsf.debug.service.IRunControl;
|
||||
import org.eclipse.cdt.dsf.debug.service.IRunControl2;
|
||||
import org.eclipse.cdt.dsf.debug.service.IBreakpoints.IBreakpointDMContext;
|
||||
import org.eclipse.cdt.dsf.debug.service.IBreakpoints.IBreakpointsTargetDMContext;
|
||||
import org.eclipse.cdt.dsf.debug.service.IBreakpointsExtension.IBreakpointHitDMEvent;
|
||||
import org.eclipse.cdt.dsf.debug.service.ICachingService;
|
||||
import org.eclipse.cdt.dsf.debug.service.IProcesses;
|
||||
import org.eclipse.cdt.dsf.debug.service.IProcesses.IProcessDMContext;
|
||||
import org.eclipse.cdt.dsf.debug.service.IProcesses.IThreadDMContext;
|
||||
import org.eclipse.cdt.dsf.debug.service.IRunControl;
|
||||
import org.eclipse.cdt.dsf.debug.service.IRunControl2;
|
||||
import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMContext;
|
||||
import org.eclipse.cdt.dsf.debug.service.command.ICommand;
|
||||
import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService;
|
||||
|
@ -50,12 +50,13 @@ import org.eclipse.cdt.dsf.mi.service.IMIProcesses;
|
|||
import org.eclipse.cdt.dsf.mi.service.IMIRunControl;
|
||||
import org.eclipse.cdt.dsf.mi.service.MIBreakpointDMData;
|
||||
import org.eclipse.cdt.dsf.mi.service.MIBreakpoints;
|
||||
import org.eclipse.cdt.dsf.mi.service.MIBreakpoints.MIBreakpointDMContext;
|
||||
import org.eclipse.cdt.dsf.mi.service.MIRunControl;
|
||||
import org.eclipse.cdt.dsf.mi.service.MIStack;
|
||||
import org.eclipse.cdt.dsf.mi.service.MIBreakpoints.MIBreakpointDMContext;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.CommandFactory;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.events.IMIDMEvent;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.events.MIBreakpointHitEvent;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.events.MICatchpointHitEvent;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.events.MIErrorEvent;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.events.MIEvent;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.events.MIInferiorExitEvent;
|
||||
|
@ -134,7 +135,9 @@ public class GDBRunControl_7_0_NS extends AbstractDsfService implements IMIRunCo
|
|||
}
|
||||
|
||||
public StateChangeReason getReason() {
|
||||
if (getMIEvent() instanceof MIBreakpointHitEvent) {
|
||||
if (getMIEvent() instanceof MICatchpointHitEvent) { // must precede MIBreakpointHitEvent
|
||||
return StateChangeReason.EVENT_BREAKPOINT;
|
||||
} else if (getMIEvent() instanceof MIBreakpointHitEvent) {
|
||||
return StateChangeReason.BREAKPOINT;
|
||||
} else if (getMIEvent() instanceof MISteppingRangeEvent) {
|
||||
return StateChangeReason.STEP;
|
||||
|
|
|
@ -73,6 +73,8 @@ public class MIBreakpointDMData implements IBreakpointDMData {
|
|||
fNature = MIBreakpointNature.TRACEPOINT;
|
||||
} else if (dsfMIBreakpoint.isWatchpoint()) {
|
||||
fNature = MIBreakpointNature.WATCHPOINT;
|
||||
} else if (dsfMIBreakpoint.isCatchpoint()) {
|
||||
fNature = MIBreakpointNature.CATCHPOINT;
|
||||
} else {
|
||||
fNature = MIBreakpointNature.BREAKPOINT;
|
||||
}
|
||||
|
@ -82,6 +84,9 @@ public class MIBreakpointDMData implements IBreakpointDMData {
|
|||
|
||||
case BREAKPOINT:
|
||||
{
|
||||
// Note that this may in fact be a catchpoint. See comment below in
|
||||
// CATCHPOINT case
|
||||
|
||||
// Generic breakpoint attributes
|
||||
fProperties.put(MIBreakpoints.BREAKPOINT_TYPE, MIBreakpoints.BREAKPOINT);
|
||||
fProperties.put(MIBreakpoints.FILE_NAME, dsfMIBreakpoint.getFile());
|
||||
|
@ -142,7 +147,27 @@ public class MIBreakpointDMData implements IBreakpointDMData {
|
|||
fProperties.put(LOCATION, formatLocation());
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
case CATCHPOINT:
|
||||
{
|
||||
// Because gdb doesn't support catchpoints in mi, we end up using
|
||||
// CLI to set the catchpoint. The sort of MIBreakpoint we create
|
||||
// at that time is minimal as the only information we get back from
|
||||
// gdb is the breakpoint number and type of the catchpoint we just
|
||||
// set. See MIBreakpoint(String)
|
||||
//
|
||||
// The only type of MIBreakpoint that will be of this CATCHPOINT type
|
||||
// is the instance we create from the response of the CLI command we
|
||||
// use to set the catchpoint. If we later query gdb for the breakpoint
|
||||
// list, we'll unfortunately end up creating an MIBreakpoint of type
|
||||
// BREAKPOINT. Maybe one day gdb will treats catchpoints like first
|
||||
// class citizens and this messy situation will go away.
|
||||
|
||||
fProperties.put(MIBreakpoints.BREAKPOINT_TYPE, MIBreakpoints.CATCHPOINT);
|
||||
fProperties.put(MIBreakpoints.CATCHPOINT_TYPE, dsfMIBreakpoint.getCatchpointType());
|
||||
break;
|
||||
}
|
||||
|
||||
// Not reachable
|
||||
default:
|
||||
{
|
||||
|
|
|
@ -40,6 +40,7 @@ import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommand
|
|||
import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.CommandFactory;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.events.MIWatchpointScopeEvent;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.output.CLICatchInfo;
|
||||
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;
|
||||
|
@ -90,6 +91,26 @@ public class MIBreakpoints extends AbstractDsfService implements IBreakpoints, I
|
|||
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$
|
||||
|
||||
// Catchpoint properties
|
||||
|
||||
/**
|
||||
* Property that indicates the kind of catchpoint (.e.g, fork call, C++
|
||||
* exception throw). Value is the gdb keyword associated with that type, as
|
||||
* listed in 'help catch'.
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
public static final String CATCHPOINT_TYPE = PREFIX + ".catchpoint_type"; //$NON-NLS-1$
|
||||
|
||||
/**
|
||||
* Property that holds arguments for the catchpoint. Value is an array of
|
||||
* Strings. Never null, but may be empty collection, as most catchpoints are
|
||||
* argument-less.
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
public static final String CATCHPOINT_ARGS = PREFIX + ".catchpoint_args"; //$NON-NLS-1$
|
||||
|
||||
// Services
|
||||
private ICommandControl fConnection;
|
||||
|
@ -151,6 +172,8 @@ public class MIBreakpoints extends AbstractDsfService implements IBreakpoints, I
|
|||
public final static String TRACEPOINT_INSERTION_FAILURE = "Tracepoint insertion failure"; //$NON-NLS-1$
|
||||
/** @since 3.0 */
|
||||
public final static String INVALID_BREAKPOINT_TYPE = "Invalid breakpoint type"; //$NON-NLS-1$
|
||||
/** @since 3.0 */
|
||||
public final static String CATCHPOINT_INSERTION_FAILURE = "Catchpoint insertion failure"; //$NON-NLS-1$
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
@ -488,6 +511,9 @@ public class MIBreakpoints extends AbstractDsfService implements IBreakpoints, I
|
|||
else if (type.equals(MIBreakpoints.TRACEPOINT)) {
|
||||
addTracepoint(context, attributes, drm);
|
||||
}
|
||||
else if (type.equals(MIBreakpoints.CATCHPOINT)) {
|
||||
addCatchpoint(context, attributes, drm);
|
||||
}
|
||||
else {
|
||||
drm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_BREAKPOINT_TYPE, null));
|
||||
drm.done();
|
||||
|
@ -526,6 +552,9 @@ public class MIBreakpoints extends AbstractDsfService implements IBreakpoints, I
|
|||
}
|
||||
|
||||
/**
|
||||
* Creates a gdb location string for a breakpoint/watchpoint/tracepoint
|
||||
* given its set of properties.
|
||||
*
|
||||
* @param attributes
|
||||
* @return
|
||||
* @since 3.0
|
||||
|
@ -740,6 +769,86 @@ public class MIBreakpoints extends AbstractDsfService implements IBreakpoints, I
|
|||
fConnection.queueCommand(fCommandFactory.createMIBreakWatch(context, isRead, isWrite, expression), addWatchpointDRM);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 3.0
|
||||
*/
|
||||
protected void addCatchpoint(final IBreakpointsTargetDMContext context, final Map<String, Object> attributes, final DataRequestMonitor<IBreakpointDMContext> finalRm) {
|
||||
// Select the context breakpoints map
|
||||
final Map<Integer, MIBreakpointDMData> contextBreakpoints = getBreakpointMap(context);
|
||||
if (contextBreakpoints == null) {
|
||||
finalRm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_BREAKPOINT_CONTEXT, null));
|
||||
finalRm.done();
|
||||
return;
|
||||
}
|
||||
|
||||
// Though CDT allows setting a temporary catchpoint, CDT never makes use of it
|
||||
assert (Boolean) getProperty(attributes, MIBreakpointDMData.IS_TEMPORARY, false) == false;
|
||||
|
||||
// GDB has no support for hardware catchpoints
|
||||
assert (Boolean) getProperty(attributes, MIBreakpointDMData.IS_HARDWARE, false) == false;
|
||||
|
||||
final String event = (String) getProperty(attributes, CATCHPOINT_TYPE, NULL_STRING);
|
||||
final String[] args = (String[]) getProperty(attributes, CATCHPOINT_ARGS, null);
|
||||
|
||||
final Step insertBreakpointStep = new Step() {
|
||||
@Override
|
||||
public void execute(final RequestMonitor rm) {
|
||||
fConnection.queueCommand(
|
||||
fCommandFactory.createCLICatch(context, event, args == null ? new String[0] : args),
|
||||
new DataRequestMonitor<CLICatchInfo>(getExecutor(), rm) {
|
||||
@Override
|
||||
protected void handleSuccess() {
|
||||
|
||||
// Sanity check
|
||||
MIBreakpoint miBkpt = getData().getMIBreakpoint();
|
||||
if (miBkpt == null) {
|
||||
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, CATCHPOINT_INSERTION_FAILURE, null));
|
||||
rm.done();
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a breakpoint object and store it in the map
|
||||
final MIBreakpointDMData newBreakpoint = new MIBreakpointDMData(miBkpt);
|
||||
int reference = newBreakpoint.getNumber();
|
||||
if (reference == -1) {
|
||||
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, CATCHPOINT_INSERTION_FAILURE, null));
|
||||
rm.done();
|
||||
return;
|
||||
}
|
||||
contextBreakpoints.put(reference, newBreakpoint);
|
||||
|
||||
// Format the return value
|
||||
MIBreakpointDMContext dmc = new MIBreakpointDMContext(MIBreakpoints.this, new IDMContext[] { context }, reference);
|
||||
finalRm.setData(dmc);
|
||||
|
||||
// Flag the event
|
||||
getSession().dispatchEvent(new BreakpointAddedEvent(dmc), getProperties());
|
||||
|
||||
// Break/Watch/Catchpoints that are disabled when set are delayed (we
|
||||
// don't tell gdb about them until the user enables them). So, we shouldn't
|
||||
// be here if this is a disabled breakpoint
|
||||
assert ((Boolean)getProperty(attributes, IS_ENABLED, true)) == true;
|
||||
|
||||
// Condition, ignore count and cannot be specified at creation time.
|
||||
// Therefore, we have to update the catchpoint 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 ));
|
||||
modifyBreakpoint(dmc, delta, rm, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleError() {
|
||||
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, CATCHPOINT_INSERTION_FAILURE, null));
|
||||
rm.done();
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
fRunControl.executeWithTargetAvailable(context, new Step[] { insertBreakpointStep }, finalRm);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// removeBreakpoint
|
||||
//-------------------------------------------------------------------------
|
||||
|
|
|
@ -30,6 +30,7 @@ import org.eclipse.cdt.debug.core.breakpointactions.BreakpointActionManager;
|
|||
import org.eclipse.cdt.debug.core.model.ICAddressBreakpoint;
|
||||
import org.eclipse.cdt.debug.core.model.ICBreakpoint;
|
||||
import org.eclipse.cdt.debug.core.model.ICBreakpointExtension;
|
||||
import org.eclipse.cdt.debug.core.model.ICEventBreakpoint;
|
||||
import org.eclipse.cdt.debug.core.model.ICLineBreakpoint;
|
||||
import org.eclipse.cdt.debug.core.model.ICTracepoint;
|
||||
import org.eclipse.cdt.debug.core.model.ICWatchpoint;
|
||||
|
@ -42,13 +43,13 @@ import org.eclipse.cdt.dsf.concurrent.ThreadSafe;
|
|||
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.IDsfBreakpointExtension;
|
||||
import org.eclipse.cdt.dsf.debug.service.IRunControl;
|
||||
import org.eclipse.cdt.dsf.debug.service.ISourceLookup;
|
||||
import org.eclipse.cdt.dsf.debug.service.IBreakpoints.IBreakpointDMContext;
|
||||
import org.eclipse.cdt.dsf.debug.service.IBreakpoints.IBreakpointsTargetDMContext;
|
||||
import org.eclipse.cdt.dsf.debug.service.IDsfBreakpointExtension;
|
||||
import org.eclipse.cdt.dsf.debug.service.IRunControl;
|
||||
import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext;
|
||||
import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext;
|
||||
import org.eclipse.cdt.dsf.debug.service.ISourceLookup;
|
||||
import org.eclipse.cdt.dsf.debug.service.ISourceLookup.ISourceLookupDMContext;
|
||||
import org.eclipse.cdt.dsf.debug.service.command.ICommandControl;
|
||||
import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlShutdownDMEvent;
|
||||
|
@ -91,7 +92,6 @@ import org.osgi.framework.BundleContext;
|
|||
*
|
||||
* It relies on MIBreakpoints for the actual back-end interface.
|
||||
*/
|
||||
@SuppressWarnings("restriction") // we use an internal platform type (BreakpointProblems)
|
||||
public class MIBreakpointsManager extends AbstractDsfService implements IBreakpointManagerListener, IBreakpointListener
|
||||
{
|
||||
// Note: Find a way to import this (careful of circular dependencies)
|
||||
|
@ -110,6 +110,30 @@ public class MIBreakpointsManager extends AbstractDsfService implements IBreakpo
|
|||
IBreakpointManager fBreakpointManager; // Platform breakpoint manager (not this!)
|
||||
BreakpointActionManager fBreakpointActionManager;
|
||||
|
||||
/**
|
||||
* A mapping of ICEventBreakpoint event types to their corresponding gdb
|
||||
* catchpoint keyword (as listed in gdb's 'help catch')
|
||||
*/
|
||||
private static final Map<String, String> sEventBkptTypeToGdb = new HashMap<String, String>();
|
||||
static {
|
||||
// these Ids are also referenced in mi.ui plugin as contribution
|
||||
// to event breakpoints selector
|
||||
sEventBkptTypeToGdb.put(ICEventBreakpoint.EVENT_TYPE_CATCH, "catch"); //$NON-NLS-1$
|
||||
sEventBkptTypeToGdb.put(ICEventBreakpoint.EVENT_TYPE_THROW, "throw"); //$NON-NLS-1$
|
||||
sEventBkptTypeToGdb.put(ICEventBreakpoint.EVENT_TYPE_SIGNAL_CATCH, "signal"); //$NON-NLS-1$
|
||||
sEventBkptTypeToGdb.put(ICEventBreakpoint.EVENT_TYPE_EXEC, "exec"); //$NON-NLS-1$
|
||||
sEventBkptTypeToGdb.put(ICEventBreakpoint.EVENT_TYPE_FORK, "fork"); //$NON-NLS-1$
|
||||
sEventBkptTypeToGdb.put(ICEventBreakpoint.EVENT_TYPE_VFORK, "vfork"); //$NON-NLS-1$
|
||||
sEventBkptTypeToGdb.put(ICEventBreakpoint.EVENT_TYPE_EXIT, "exit"); //$NON-NLS-1$
|
||||
sEventBkptTypeToGdb.put(ICEventBreakpoint.EVENT_TYPE_PROCESS_START, "start"); //$NON-NLS-1$
|
||||
sEventBkptTypeToGdb.put(ICEventBreakpoint.EVENT_TYPE_PROCESS_STOP, "stop"); //$NON-NLS-1$
|
||||
sEventBkptTypeToGdb.put(ICEventBreakpoint.EVENT_TYPE_THREAD_START, "thread_start"); //$NON-NLS-1$
|
||||
sEventBkptTypeToGdb.put(ICEventBreakpoint.EVENT_TYPE_THREAD_EXIT, "thread_exit"); //$NON-NLS-1$
|
||||
sEventBkptTypeToGdb.put(ICEventBreakpoint.EVENT_TYPE_THREAD_JOIN, "thread_join"); //$NON-NLS-1$
|
||||
sEventBkptTypeToGdb.put(ICEventBreakpoint.EVENT_TYPE_LIBRARY_LOAD, "load"); //$NON-NLS-1$
|
||||
sEventBkptTypeToGdb.put(ICEventBreakpoint.EVENT_TYPE_LIBRARY_UNLOAD, "unload"); //$NON-NLS-1$
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Breakpoints tracking
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
@ -1230,6 +1254,7 @@ public class MIBreakpointsManager extends AbstractDsfService implements IBreakpo
|
|||
public void eventDispatched(SuspendedEvent e) {
|
||||
|
||||
if (e.getMIEvent() instanceof MIBreakpointHitEvent) {
|
||||
// This covers catchpoints, too
|
||||
MIBreakpointHitEvent evt = (MIBreakpointHitEvent) e.getMIEvent();
|
||||
performBreakpointAction(evt.getDMContext(), evt.getNumber());
|
||||
return;
|
||||
|
@ -1673,8 +1698,24 @@ public class MIBreakpointsManager extends AbstractDsfService implements IBreakpo
|
|||
properties.put(MIBreakpoints.BREAKPOINT_TYPE, MIBreakpoints.TRACEPOINT);
|
||||
properties.put(MIBreakpoints.PASS_COUNT, attributes.get(ICTracepoint.PASS_COUNT));
|
||||
}
|
||||
} else {
|
||||
// catchpoint?
|
||||
}
|
||||
else if (breakpoint instanceof ICEventBreakpoint) {
|
||||
properties.put(MIBreakpoints.BREAKPOINT_TYPE, MIBreakpoints.CATCHPOINT);
|
||||
properties.put(MIBreakpoints.CATCHPOINT_TYPE, sEventBkptTypeToGdb.get(attributes.get(ICEventBreakpoint.EVENT_TYPE_ID)));
|
||||
|
||||
String arg = (String)attributes.get(ICEventBreakpoint.EVENT_ARG);
|
||||
String[] args;
|
||||
if ((arg != null) && (arg.length() != 0)) {
|
||||
args = new String[1];
|
||||
args[0] = arg;
|
||||
}
|
||||
else {
|
||||
args = new String[0];
|
||||
}
|
||||
properties.put(MIBreakpoints.CATCHPOINT_ARGS, args);
|
||||
}
|
||||
else {
|
||||
assert false : "platform breakpoint is of an unexpected type: " + breakpoint.getClass().getName(); //$NON-NLS-1$
|
||||
}
|
||||
|
||||
// Common fields
|
||||
|
|
|
@ -28,11 +28,11 @@ import org.eclipse.cdt.dsf.datamodel.DMContexts;
|
|||
import org.eclipse.cdt.dsf.datamodel.IDMContext;
|
||||
import org.eclipse.cdt.dsf.datamodel.IDMEvent;
|
||||
import org.eclipse.cdt.dsf.debug.service.IBreakpoints;
|
||||
import org.eclipse.cdt.dsf.debug.service.ICachingService;
|
||||
import org.eclipse.cdt.dsf.debug.service.IProcesses;
|
||||
import org.eclipse.cdt.dsf.debug.service.IBreakpoints.IBreakpointDMContext;
|
||||
import org.eclipse.cdt.dsf.debug.service.IBreakpoints.IBreakpointsTargetDMContext;
|
||||
import org.eclipse.cdt.dsf.debug.service.IBreakpointsExtension.IBreakpointHitDMEvent;
|
||||
import org.eclipse.cdt.dsf.debug.service.ICachingService;
|
||||
import org.eclipse.cdt.dsf.debug.service.IProcesses;
|
||||
import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMContext;
|
||||
import org.eclipse.cdt.dsf.debug.service.command.BufferedCommandControl;
|
||||
import org.eclipse.cdt.dsf.debug.service.command.CommandCache;
|
||||
|
@ -44,6 +44,7 @@ import org.eclipse.cdt.dsf.mi.service.MIBreakpoints.MIBreakpointDMContext;
|
|||
import org.eclipse.cdt.dsf.mi.service.command.CommandFactory;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.events.IMIDMEvent;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.events.MIBreakpointHitEvent;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.events.MICatchpointHitEvent;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.events.MIErrorEvent;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.events.MIEvent;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.events.MIRunningEvent;
|
||||
|
@ -171,7 +172,9 @@ public class MIRunControl extends AbstractDsfService implements IMIRunControl, I
|
|||
}
|
||||
|
||||
public StateChangeReason getReason() {
|
||||
if (getMIEvent() instanceof MIBreakpointHitEvent) {
|
||||
if (getMIEvent() instanceof MICatchpointHitEvent) { // must precede MIBreakpointHitEvent
|
||||
return StateChangeReason.EVENT_BREAKPOINT;
|
||||
} else if (getMIEvent() instanceof MIBreakpointHitEvent) {
|
||||
return StateChangeReason.BREAKPOINT;
|
||||
} else if (getMIEvent() instanceof MISteppingRangeEvent) {
|
||||
return StateChangeReason.STEP;
|
||||
|
|
|
@ -20,6 +20,7 @@ import java.io.OutputStream;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
|
@ -50,6 +51,7 @@ import org.eclipse.cdt.dsf.mi.service.command.output.MIOutput;
|
|||
import org.eclipse.cdt.dsf.mi.service.command.output.MIParser;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.output.MIResult;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.output.MIResultRecord;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.output.MIStreamRecord;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.output.MIValue;
|
||||
import org.eclipse.cdt.dsf.service.AbstractDsfService;
|
||||
import org.eclipse.cdt.dsf.service.DsfSession;
|
||||
|
@ -604,12 +606,20 @@ public abstract class AbstractMIControl extends AbstractDsfService
|
|||
private final InputStream fInputStream;
|
||||
private final MIParser fMiParser = new MIParser();
|
||||
|
||||
/**
|
||||
* List of out of band records since the last result record. Out of band records are
|
||||
* required for processing the results of CLI commands.
|
||||
*/
|
||||
private final List<MIOOBRecord> fAccumulatedOOBRecords = new ArrayList<MIOOBRecord>();
|
||||
|
||||
/**
|
||||
* List of out of band records since the last result record. Out of band
|
||||
* records are required for processing the results of CLI commands.
|
||||
*/
|
||||
private final List<MIOOBRecord> fAccumulatedOOBRecords = new LinkedList<MIOOBRecord>();
|
||||
|
||||
/**
|
||||
* List of stream records since the last result record, not including
|
||||
* the record currently being processed (if it's a stream one). This is
|
||||
* a subset of {@link #fAccumulatedOOBRecords}, as a stream record is a
|
||||
* particular type of OOB record.
|
||||
*/
|
||||
private final List<MIStreamRecord> fAccumulatedStreamRecords = new LinkedList<MIStreamRecord>();
|
||||
|
||||
public RxThread(InputStream inputStream) {
|
||||
super("MI RX Thread"); //$NON-NLS-1$
|
||||
fInputStream = inputStream;
|
||||
|
@ -784,6 +794,7 @@ public abstract class AbstractMIControl extends AbstractDsfService
|
|||
final MIOutput response = new MIOutput(
|
||||
rr, fAccumulatedOOBRecords.toArray(new MIOOBRecord[fAccumulatedOOBRecords.size()]) );
|
||||
fAccumulatedOOBRecords.clear();
|
||||
fAccumulatedStreamRecords.clear();
|
||||
|
||||
MIInfo result = commandHandle.getCommand().getResult(response);
|
||||
DataRequestMonitor<MIInfo> rm = commandHandle.getRequestMonitor();
|
||||
|
@ -870,12 +881,28 @@ public abstract class AbstractMIControl extends AbstractDsfService
|
|||
} else if (recordType == MIParser.RecordType.OOBRecord) {
|
||||
// Process OOBs
|
||||
final MIOOBRecord oob = fMiParser.parseMIOOBRecord(line);
|
||||
if (!fRxCommands.isEmpty()) {
|
||||
// This is for CLI commands, so only store if we are actually
|
||||
// waiting for a command reply
|
||||
fAccumulatedOOBRecords.add(oob);
|
||||
|
||||
fAccumulatedOOBRecords.add(oob);
|
||||
if (fAccumulatedOOBRecords.size() > 20) { // limit growth; see bug 302927
|
||||
fAccumulatedOOBRecords.remove(0);
|
||||
}
|
||||
final MIOutput response = new MIOutput(oob);
|
||||
|
||||
// The handling of this OOB record may need the stream records
|
||||
// that preceded it. One such case is a stopped event caused by a
|
||||
// catchpoint in gdb < 7.0. The stopped event provides no
|
||||
// reason, but we can determine it was caused by a catchpoint by
|
||||
// looking at the target stream.
|
||||
|
||||
final MIOutput response = new MIOutput(oob, fAccumulatedStreamRecords.toArray(new MIStreamRecord[fAccumulatedStreamRecords.size()]));
|
||||
|
||||
// If this is a stream record, add it to the accumulated bucket
|
||||
// for possible use in handling a future OOB (see comment above)
|
||||
if (oob instanceof MIStreamRecord) {
|
||||
fAccumulatedStreamRecords.add((MIStreamRecord)oob);
|
||||
if (fAccumulatedStreamRecords.size() > 20) { // limit growth; see bug 302927
|
||||
fAccumulatedStreamRecords.remove(0);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* OOBS are events. So we pass them to any event listeners who want to see them. Again this must
|
||||
|
|
|
@ -27,6 +27,7 @@ import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommand
|
|||
import org.eclipse.cdt.dsf.gdb.service.IGDBTraceControl.ITraceTargetDMContext;
|
||||
import org.eclipse.cdt.dsf.mi.service.IMIExecutionDMContext;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.commands.CLIAttach;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.commands.CLICatch;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.commands.CLIDetach;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.commands.CLIExecAbort;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.commands.CLIInfoSharedLibrary;
|
||||
|
@ -122,6 +123,7 @@ import org.eclipse.cdt.dsf.mi.service.command.commands.MIVarSetFormat;
|
|||
import org.eclipse.cdt.dsf.mi.service.command.commands.MIVarShowAttributes;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.commands.MIVarShowFormat;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.commands.MIVarUpdate;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.output.CLICatchInfo;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.output.CLIInfoSharedLibraryInfo;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.output.CLIInfoThreadsInfo;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.output.CLIThreadInfo;
|
||||
|
@ -176,6 +178,10 @@ public class CommandFactory {
|
|||
return new CLIAttach(ctx, pid);
|
||||
}
|
||||
|
||||
public ICommand<CLICatchInfo> createCLICatch(IBreakpointsTargetDMContext ctx, String event, String[] args) {
|
||||
return new CLICatch(ctx, event, args);
|
||||
}
|
||||
|
||||
public ICommand<MIInfo> createCLIDetach(IDMContext ctx) {
|
||||
return new CLIDetach(ctx);
|
||||
}
|
||||
|
|
|
@ -14,17 +14,17 @@ package org.eclipse.cdt.dsf.mi.service.command;
|
|||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.cdt.dsf.debug.service.IRunControl;
|
||||
import org.eclipse.cdt.dsf.debug.service.IProcesses.IProcessDMContext;
|
||||
import org.eclipse.cdt.dsf.debug.service.IProcesses.IThreadDMContext;
|
||||
import org.eclipse.cdt.dsf.debug.service.IRunControl;
|
||||
import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext;
|
||||
import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext;
|
||||
import org.eclipse.cdt.dsf.debug.service.command.ICommand;
|
||||
import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlDMContext;
|
||||
import org.eclipse.cdt.dsf.debug.service.command.ICommandListener;
|
||||
import org.eclipse.cdt.dsf.debug.service.command.ICommandResult;
|
||||
import org.eclipse.cdt.dsf.debug.service.command.ICommandToken;
|
||||
import org.eclipse.cdt.dsf.debug.service.command.IEventListener;
|
||||
import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlDMContext;
|
||||
import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin;
|
||||
import org.eclipse.cdt.dsf.gdb.service.IGDBBackend;
|
||||
import org.eclipse.cdt.dsf.mi.service.IMIProcesses;
|
||||
|
@ -40,6 +40,7 @@ import org.eclipse.cdt.dsf.mi.service.command.commands.MIExecStep;
|
|||
import org.eclipse.cdt.dsf.mi.service.command.commands.MIExecStepInstruction;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.commands.MIExecUntil;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.events.MIBreakpointHitEvent;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.events.MICatchpointHitEvent;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.events.MIEvent;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.events.MIFunctionFinishedEvent;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.events.MIInferiorExitEvent;
|
||||
|
@ -58,6 +59,7 @@ import org.eclipse.cdt.dsf.mi.service.command.output.MIOOBRecord;
|
|||
import org.eclipse.cdt.dsf.mi.service.command.output.MIOutput;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.output.MIResult;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.output.MIResultRecord;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.output.MIStreamRecord;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.output.MIValue;
|
||||
import org.eclipse.cdt.dsf.service.DsfServicesTracker;
|
||||
|
||||
|
@ -136,6 +138,21 @@ public class MIRunControlEventProcessor
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GDB < 7.0 does not provide a reason when stopping on a
|
||||
// catchpoint. However, the reason is contained in the
|
||||
// stream records that precede the exec async output one.
|
||||
// This is ugly, but we don't really have an alternative.
|
||||
if (events.isEmpty()) {
|
||||
MIStreamRecord[] streamRecords = ((MIOutput)output).getStreamRecords();
|
||||
for (MIStreamRecord streamRecord : streamRecords) {
|
||||
String log = streamRecord.getString();
|
||||
if (log.startsWith("Catchpoint ")) { //$NON-NLS-1$
|
||||
events.add(MICatchpointHitEvent.parse(getExecutionContext(exec), exec.getToken(), results, streamRecord));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We were stopped for some unknown reason, for example
|
||||
// GDB for temporary breakpoints will not send the
|
||||
// "reason" ??? still fire a stopped event.
|
||||
|
@ -153,8 +170,13 @@ public class MIRunControlEventProcessor
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected MIEvent<?> createEvent(String reason, MIExecAsyncOutput exec) {
|
||||
|
||||
/**
|
||||
* Create an execution context given an exec-async-output OOB record
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
protected IExecutionDMContext getExecutionContext(MIExecAsyncOutput exec) {
|
||||
String threadId = null;
|
||||
|
||||
MIResult[] results = exec.getMIResults();
|
||||
|
@ -185,6 +207,11 @@ public class MIRunControlEventProcessor
|
|||
execDmc = procService.createExecutionContext(processContainerDmc, threadDmc, threadId);
|
||||
}
|
||||
|
||||
return execDmc;
|
||||
}
|
||||
|
||||
protected MIEvent<?> createEvent(String reason, MIExecAsyncOutput exec) {
|
||||
IExecutionDMContext execDmc = getExecutionContext(exec);
|
||||
MIEvent<?> event = null;
|
||||
if ("breakpoint-hit".equals(reason)) { //$NON-NLS-1$
|
||||
event = MIBreakpointHitEvent.parse(execDmc, exec.getToken(), exec.getMIResults());
|
||||
|
|
|
@ -60,6 +60,7 @@ import org.eclipse.cdt.dsf.mi.service.command.output.MIOOBRecord;
|
|||
import org.eclipse.cdt.dsf.mi.service.command.output.MIOutput;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.output.MIResult;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.output.MIResultRecord;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.output.MIStreamRecord;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.output.MIValue;
|
||||
import org.eclipse.cdt.dsf.service.DsfServicesTracker;
|
||||
|
||||
|
@ -132,7 +133,7 @@ public class MIRunControlEventProcessor_7_0
|
|||
if (var.equals("reason")) { //$NON-NLS-1$
|
||||
if (val instanceof MIConst) {
|
||||
String reason = ((MIConst) val).getString();
|
||||
MIEvent<?> e = createEvent(reason, exec);
|
||||
MIEvent<?> e = createEvent(reason, exec, ((MIOutput)output).getStreamRecords());
|
||||
if (e != null) {
|
||||
events.add(e);
|
||||
continue;
|
||||
|
@ -144,7 +145,7 @@ public class MIRunControlEventProcessor_7_0
|
|||
// GDB for temporary breakpoints will not send the
|
||||
// "reason" ??? still fire a stopped event.
|
||||
if (events.isEmpty()) {
|
||||
MIEvent<?> e = createEvent(STOPPED_REASON, exec);
|
||||
MIEvent<?> e = createEvent(STOPPED_REASON, exec, ((MIOutput)output).getStreamRecords());
|
||||
if (e != null) {
|
||||
events.add(e);
|
||||
}
|
||||
|
@ -155,7 +156,7 @@ public class MIRunControlEventProcessor_7_0
|
|||
}
|
||||
}
|
||||
else if (RUNNING_REASON.equals(state)) {
|
||||
MIEvent<?> event = createEvent(RUNNING_REASON, exec);
|
||||
MIEvent<?> event = createEvent(RUNNING_REASON, exec, ((MIOutput)output).getStreamRecords());
|
||||
if (event != null) {
|
||||
fCommandControl.getSession().dispatchEvent(event, fCommandControl.getProperties());
|
||||
}
|
||||
|
@ -240,8 +241,18 @@ public class MIRunControlEventProcessor_7_0
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected MIEvent<?> createEvent(String reason, MIExecAsyncOutput exec) {
|
||||
|
||||
/**
|
||||
* @since 3.0
|
||||
* @param miStreamRecords
|
||||
* the stream records that preceded 'exec'. Determining which
|
||||
* type of event to create may require additional insight
|
||||
* available in those records. One example is catchpoint hits.
|
||||
* They are reported by gdb (>= 7.0)as a simple breakpoint hit.
|
||||
* However, gdb also sends a stream record that reveals that it's
|
||||
* a catchpoint hit.
|
||||
*/
|
||||
protected MIEvent<?> createEvent(String reason, MIExecAsyncOutput exec, MIStreamRecord[] miStreamRecords) {
|
||||
MIEvent<?> event = null;
|
||||
|
||||
if ("exited-normally".equals(reason) || "exited".equals(reason)) { //$NON-NLS-1$ //$NON-NLS-2$
|
||||
|
@ -300,7 +311,7 @@ public class MIRunControlEventProcessor_7_0
|
|||
}
|
||||
|
||||
if ("breakpoint-hit".equals(reason)) { //$NON-NLS-1$
|
||||
event = MIBreakpointHitEvent.parse(execDmc, exec.getToken(), exec.getMIResults());
|
||||
event = MIBreakpointHitEvent.parse(execDmc, exec.getToken(), exec.getMIResults(), miStreamRecords);
|
||||
} else if (
|
||||
"watchpoint-trigger".equals(reason) //$NON-NLS-1$
|
||||
|| "read-watchpoint-trigger".equals(reason) //$NON-NLS-1$
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2010 Freescale Semiconductor 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:
|
||||
* Freescale Semiconductor - Initial API and implementation
|
||||
*******************************************************************************/
|
||||
|
||||
package org.eclipse.cdt.dsf.mi.service.command.commands;
|
||||
|
||||
import org.eclipse.cdt.dsf.debug.service.IBreakpoints.IBreakpointsTargetDMContext;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.output.CLICatchInfo;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.output.MIOutput;
|
||||
|
||||
/**
|
||||
* gdb 'catch' command. Even though the command has been around since gdb 6.6,
|
||||
* it's still not supported in gdb 7.0 MI.
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
public class CLICatch extends CLICommand<CLICatchInfo> {
|
||||
|
||||
private static String formOperation(String event, String[] args) {
|
||||
StringBuilder oper = new StringBuilder("catch " + event); //$NON-NLS-1$
|
||||
for (String arg : args) {
|
||||
oper.append(" " + arg); //$NON-NLS-1$
|
||||
}
|
||||
return oper.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param ctx the context for the command
|
||||
* @param event the type of event to be caught; one of the keywords documented in 'help catch'
|
||||
* @param args zero or more arguments particular to the 'event'
|
||||
*/
|
||||
public CLICatch(IBreakpointsTargetDMContext ctx, String event, String[] args) {
|
||||
super(ctx, formOperation(event, args));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.eclipse.cdt.dsf.mi.service.command.commands.MICommand#getResult(org.eclipse.cdt.dsf.mi.service.command.output.MIOutput)
|
||||
*/
|
||||
@Override
|
||||
public MIInfo getResult(MIOutput MIresult) {
|
||||
return new CLICatchInfo(MIresult);
|
||||
}
|
||||
}
|
|
@ -17,11 +17,17 @@ import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext;
|
|||
import org.eclipse.cdt.dsf.mi.service.command.output.MIConst;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.output.MIFrame;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.output.MIResult;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.output.MIStreamRecord;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.output.MIValue;
|
||||
|
||||
/**
|
||||
* ^stopped,reason="breakpoint-hit",bkptno="1",thread-id="0",frame={addr="0x08048468",func="main",args=[{name="argc",value="1"},{name="argv",value="0xbffff18c"}],file="hello.c",line="4"}
|
||||
*
|
||||
* Conveys that gdb reported the target stopped because of a breakpoint. This
|
||||
* includes catchpoints, as gdb reports them as a breakpoint-hit. The
|
||||
* async-exec-output record looks like this:
|
||||
*
|
||||
* <code>
|
||||
* ^stopped,reason="breakpoint-hit",bkptno="1",thread-id="0",frame={addr="0x08048468",func="main",args=[{name="argc",value="1"},{name="argv",value="0xbffff18c"}],file="hello.c",line="4"}
|
||||
* </code>
|
||||
*/
|
||||
@Immutable
|
||||
public class MIBreakpointHitEvent extends MIStoppedEvent {
|
||||
|
@ -38,9 +44,10 @@ public class MIBreakpointHitEvent extends MIStoppedEvent {
|
|||
}
|
||||
|
||||
/**
|
||||
* @since 1.1
|
||||
* @param miStreamRecords
|
||||
* @since 3.0
|
||||
*/
|
||||
public static MIBreakpointHitEvent parse(IExecutionDMContext dmc, int token, MIResult[] results)
|
||||
public static MIBreakpointHitEvent parse(IExecutionDMContext dmc, int token, MIResult[] results, MIStreamRecord[] miStreamRecords)
|
||||
{
|
||||
int bkptno = -1;
|
||||
|
||||
|
@ -60,6 +67,14 @@ public class MIBreakpointHitEvent extends MIStoppedEvent {
|
|||
}
|
||||
}
|
||||
MIStoppedEvent stoppedEvent = MIStoppedEvent.parse(dmc, token, results);
|
||||
|
||||
for (MIStreamRecord streamRecord : miStreamRecords) {
|
||||
String log = streamRecord.getString();
|
||||
if (log.startsWith("Catchpoint ")) { //$NON-NLS-1$
|
||||
return new MICatchpointHitEvent(stoppedEvent.getDMContext(), token, results, stoppedEvent.getFrame(), bkptno);
|
||||
}
|
||||
}
|
||||
|
||||
return new MIBreakpointHitEvent(stoppedEvent.getDMContext(), token, results, stoppedEvent.getFrame(), bkptno);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
package org.eclipse.cdt.dsf.mi.service.command.events;
|
||||
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.output.MIFrame;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.output.MIResult;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.output.MIStreamRecord;
|
||||
|
||||
/**
|
||||
* @since 3.0
|
||||
*/
|
||||
public class MICatchpointHitEvent extends MIBreakpointHitEvent {
|
||||
|
||||
protected MICatchpointHitEvent(IExecutionDMContext ctx, int token,
|
||||
MIResult[] results, MIFrame frame, int bkptno) {
|
||||
super(ctx, token, results, frame, bkptno);
|
||||
}
|
||||
|
||||
/**
|
||||
* This variant is for catchpoint-hit in gdb < 7.0. For those versions, gdb
|
||||
* sends us a stopped event, but it doesn't include a reason in it.
|
||||
* Fortunately, it does output a stream record that tells us not only that a
|
||||
* catchpoint was hit, but what its breakpoint number is.
|
||||
*
|
||||
* @param streamRecord
|
||||
* the stream record that reveals that a catchpoint was hit
|
||||
*/
|
||||
public static MIBreakpointHitEvent parse(IExecutionDMContext dmc, int token, MIResult[] results, MIStreamRecord streamRecord) {
|
||||
// stream record example: "Catchpoint 1 (exception caught)"
|
||||
StringTokenizer tokenizer = new StringTokenizer(streamRecord.getString());
|
||||
tokenizer.nextToken(); // "Catchpoint"
|
||||
try {
|
||||
int bkptNumber = Integer.parseInt(tokenizer.nextToken()); // "1" (e.g.,)
|
||||
MIStoppedEvent stoppedEvent = MIStoppedEvent.parse(dmc, token, results);
|
||||
return new MICatchpointHitEvent(stoppedEvent.getDMContext(), token, results, stoppedEvent.getFrame(), bkptNumber);
|
||||
}
|
||||
catch (NumberFormatException exc) {
|
||||
assert false : "unexpected catchpoint stream record format: " + streamRecord.getString(); //$NON-NLS-1$
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2010 Freescale Semiconductor 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:
|
||||
* Freescale Semiconductor - Initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.eclipse.cdt.dsf.mi.service.command.output;
|
||||
|
||||
/**
|
||||
* Processes the result of a gdb 'catch' command. Even though the command has
|
||||
* been around since gdb 6.6, it's still not supported in gdb 7.0 MI.
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
public class CLICatchInfo extends MIInfo {
|
||||
private MIBreakpoint fMiBreakpoint;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param record the result object for the command
|
||||
*/
|
||||
public CLICatchInfo(MIOutput record) {
|
||||
super(record);
|
||||
assert record != null;
|
||||
parse();
|
||||
}
|
||||
|
||||
/**
|
||||
* sample output: Catchpoint 3 (catch)
|
||||
*/
|
||||
protected void parse() {
|
||||
if (isDone()) {
|
||||
MIOutput out = getMIOutput();
|
||||
for (MIOOBRecord oob : out.getMIOOBRecords()) {
|
||||
if (oob instanceof MIConsoleStreamOutput) {
|
||||
// We are interested in the catchpoint info
|
||||
fMiBreakpoint = parseCatchpoint(((MIConsoleStreamOutput)oob).getString().trim());
|
||||
if (fMiBreakpoint != null) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private MIBreakpoint parseCatchpoint(String str) {
|
||||
if (str.startsWith("Catchpoint ")) { //$NON-NLS-1$
|
||||
return new MIBreakpoint(str);
|
||||
}
|
||||
assert false : "CLI catch command had an unexpected result: " + str; //$NON-NLS-1$
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an MIBreakpoint object for the catchpoint that was created.
|
||||
*
|
||||
* @return an MIBreakpoint object or null if the command result had
|
||||
* unexpected data
|
||||
*/
|
||||
public MIBreakpoint getMIBreakpoint() {
|
||||
return fMiBreakpoint;
|
||||
}
|
||||
}
|
|
@ -14,6 +14,8 @@
|
|||
|
||||
package org.eclipse.cdt.dsf.mi.service.command.output;
|
||||
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
/**
|
||||
* Contain info about the GDB/MI breakpoint.
|
||||
*
|
||||
|
@ -78,7 +80,13 @@ public class MIBreakpoint {
|
|||
// Indicate if we are dealing with a tracepoint.
|
||||
// (if its a fast or slow tracepoint can be known through the 'type' field)
|
||||
boolean isTpt = false;
|
||||
|
||||
|
||||
/** See {@link #isCatchpoint()} */
|
||||
boolean isCatchpoint;
|
||||
|
||||
/** See {@link #getCatchpointType()} */
|
||||
private String catchpointType;
|
||||
|
||||
public MIBreakpoint() {
|
||||
}
|
||||
|
||||
|
@ -105,12 +113,80 @@ public class MIBreakpoint {
|
|||
isWWpt = other.isWWpt;
|
||||
isHdw = other.isHdw;
|
||||
isTpt = other.isTpt;
|
||||
isCatchpoint = other.isCatchpoint;
|
||||
catchpointType = other.catchpointType;
|
||||
}
|
||||
|
||||
public MIBreakpoint(MITuple tuple) {
|
||||
parse(tuple);
|
||||
}
|
||||
|
||||
/**
|
||||
* This constructor is used for catchpoints. Catchpoints are not yet
|
||||
* supported in MI, so we end up using CLI.
|
||||
*
|
||||
* <p>
|
||||
* Note that this poses at least one challenge for us. Normally, upon
|
||||
* creating a breakpoint/watchpoint/tracepoint via mi, we get back a command
|
||||
* result from gdb that contains all the details of the newly created
|
||||
* object, and we use that detailed information to create the MIBreakpoint.
|
||||
* This is the same data we'll get back if we later ask gdb for the
|
||||
* breakpoint list. However, because we're using CLI for cathpoints, we
|
||||
* don't get that detailed information from gdb at creation time, but the
|
||||
* detail will be there if we later ask for the breakpoint list. What this
|
||||
* all means is that we can't compare the two MIBreakponts (the one we
|
||||
* construct at creation time, and the one we get by asking gdb for the
|
||||
* breakpoint list). The most we can do is compare the breakpoint number.
|
||||
* That for sure should be the same.
|
||||
*
|
||||
* <p>
|
||||
* The detail we get from querying the breakpoint list, BTW, won't even
|
||||
* reveal that it's a catchpoint. gdb simply reports it as a breakpoint,
|
||||
* probably because that's what it really sets under the cover--an address
|
||||
* breakpoint. This is another thing we need to keep in mind and creatively
|
||||
* deal with. When we set the catchpoint, this constructor is used. When the
|
||||
* breakpoint list is queried, {@link #MIBreakpoint(MITuple)} is used for
|
||||
* that same breakpoint number, and a consumer of that MIBreakpoint won't be
|
||||
* able to tell it's a catchpoint. Quite the mess. Wish gdb would treat
|
||||
* catchpoints like first class citizens.
|
||||
*
|
||||
* @param cliResult
|
||||
* the output from the CLI command. Example:
|
||||
* "Catchpoint 1 (catch)"
|
||||
* @since 3.0
|
||||
*/
|
||||
public MIBreakpoint(String cliResult) {
|
||||
if (cliResult.startsWith("Catchpoint ")) { //$NON-NLS-1$
|
||||
int bkptNumber = 0;
|
||||
|
||||
StringTokenizer tokenizer = new StringTokenizer(cliResult);
|
||||
for (int i = 0; tokenizer.hasMoreTokens(); i++) {
|
||||
String sub = tokenizer.nextToken();
|
||||
switch (i) {
|
||||
case 0: // first token is "Catchpoint"
|
||||
break;
|
||||
case 1: // second token is the breakpoint number
|
||||
bkptNumber = Integer.parseInt(sub);
|
||||
break;
|
||||
case 2: // third token is the event type; drop the parenthesis
|
||||
if (sub.startsWith("(")) { //$NON-NLS-1$
|
||||
sub = sub.substring(1, sub.length()-1);
|
||||
}
|
||||
catchpointType = sub;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
number = bkptNumber;
|
||||
isCatchpoint = true;
|
||||
enabled = true;
|
||||
}
|
||||
else {
|
||||
assert false : "unexpected CLI output: " + cliResult; //$NON-NLS-1$
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Properties getters
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
@ -183,6 +259,17 @@ public class MIBreakpoint {
|
|||
return exp;
|
||||
}
|
||||
|
||||
/**
|
||||
* If isCatchpoint is true, then this indicates the type of catchpoint
|
||||
* (event), as reported by gdb in its response to the CLI catch command.
|
||||
* E.g., 'catch' or 'fork'
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
public String getCatchpointType() {
|
||||
return catchpointType;
|
||||
}
|
||||
|
||||
public boolean isTemporary() {
|
||||
return getDisposition().equals("del"); //$NON-NLS-1$
|
||||
}
|
||||
|
@ -247,6 +334,15 @@ public class MIBreakpoint {
|
|||
public boolean isTracepoint() {
|
||||
return isTpt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if we are dealing with a catchpoint.
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
public boolean isCatchpoint() {
|
||||
return isCatchpoint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the passcount of a tracepoint. Will return 0 if this
|
||||
|
@ -303,6 +399,11 @@ public class MIBreakpoint {
|
|||
} catch (NumberFormatException e) {
|
||||
}
|
||||
} else if (var.equals("type")) { //$NON-NLS-1$
|
||||
// Note that catchpoints are reported by gdb as address
|
||||
// breakpoints; there's really nothing we can go on to determine
|
||||
// that it's actually a catchpoint (short of using a really ugly
|
||||
// and fragile hack--looking at the 'what' field for specific values)
|
||||
|
||||
type = str;
|
||||
//type="hw watchpoint"
|
||||
if (type.startsWith("hw")) { //$NON-NLS-1$
|
||||
|
@ -370,5 +471,4 @@ public class MIBreakpoint {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -20,17 +20,54 @@ public class MIOutput {
|
|||
|
||||
private final MIResultRecord rr;
|
||||
private final MIOOBRecord[] oobs;
|
||||
private MIStreamRecord[] streamRecords;
|
||||
|
||||
public MIOutput() {
|
||||
this(null, null);
|
||||
this(null, (MIOOBRecord[])null);
|
||||
}
|
||||
|
||||
public MIOutput(MIOOBRecord oob) {
|
||||
/**
|
||||
* @param oob
|
||||
* @deprecated Use {@link #MIOutput(MIOOBRecord, MIStreamRecord[])}
|
||||
*/
|
||||
@Deprecated
|
||||
public MIOutput(MIOOBRecord oob) {
|
||||
this(null, new MIOOBRecord[] { oob });
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Constructor used when handling a single out-of-band record
|
||||
*
|
||||
* @param the
|
||||
* out-of-bound record
|
||||
* @param streamRecords
|
||||
* any stream records that preceded the out-of-bound record,
|
||||
* since the last command result. This need not contain *all*
|
||||
* such records if it's been a while since the last command (for
|
||||
* practical reasons, there is a cap on the number of stream
|
||||
* records that are remembered). This will have the most recent
|
||||
* records. Must not be null; may be empty
|
||||
* @since 3.0
|
||||
*/
|
||||
public MIOutput(MIOOBRecord oob, MIStreamRecord[] streamRecords) {
|
||||
this(null, new MIOOBRecord[] { oob });
|
||||
this.streamRecords = streamRecords;
|
||||
assert streamRecords != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor used when handling a command result.
|
||||
*
|
||||
* @param rr
|
||||
* the result record
|
||||
* @param oobs
|
||||
* any out-of-band records that preceded this particular command
|
||||
* result. This need not contain *all* such records if it's been
|
||||
* a while since the last command (for practical reasons, there
|
||||
* is a cap on the number of OOB records that are remembered).
|
||||
* This will have the most recent records.
|
||||
*
|
||||
*/
|
||||
public MIOutput(MIResultRecord rr, MIOOBRecord[] oobs) {
|
||||
this.rr = rr;
|
||||
this.oobs = oobs;
|
||||
|
@ -44,6 +81,20 @@ public class MIOutput {
|
|||
return oobs;
|
||||
}
|
||||
|
||||
/**
|
||||
* See param in {@link #MIOutput(MIOOBRecord, MIStreamRecord[])}
|
||||
*
|
||||
* @return Only instances created for an OOB record will have stream
|
||||
* records; may be an empty collection in that case, but not null.
|
||||
* Instances created for a command result will return null from this
|
||||
* method. Stream records can be retrieved from
|
||||
* {@link #getMIOOBRecords()} in that case.
|
||||
* @since 3.0
|
||||
*/
|
||||
public MIStreamRecord[] getStreamRecords() {
|
||||
return streamRecords;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
#include <iostream>
|
||||
#include <unistd.h>
|
||||
int g_i = 0;
|
||||
int main() {
|
||||
for (; g_i < 8; g_i++) {
|
||||
try {
|
||||
std::cout << "Throwing exception" << std::endl;
|
||||
throw 1;
|
||||
}
|
||||
catch (int exc) {
|
||||
std::cout << "Exception caught" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
// For setting a catchpoint while target is running
|
||||
std::cout << "Sleeping..." << std::endl;
|
||||
sleep(2);
|
||||
std::cout << "...awake!" << std::endl;
|
||||
try {
|
||||
std::cout << "Throwing exception" << std::endl;
|
||||
throw 1;
|
||||
}
|
||||
catch (int exc) {
|
||||
std::cout << "Exception caught" << std::endl;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -72,7 +72,12 @@ public class ExecutionContextLabelText extends LabelText {
|
|||
reasonLabel = MessagesForLaunchVM.State_change_reason__User_request__label;
|
||||
} else if (StateChangeReason.WATCHPOINT.name().equals(reason)) {
|
||||
reasonLabel = MessagesForLaunchVM.State_change_reason__Watchpoint__label;
|
||||
} else if (StateChangeReason.EVENT_BREAKPOINT.name().equals(reason)) {
|
||||
reasonLabel = MessagesForLaunchVM.State_change_reason__EventBreakpoint__label;
|
||||
} else {
|
||||
assert false : "unexpected state change reason: " + reason; //$NON-NLS-1$
|
||||
}
|
||||
|
||||
return reasonLabel;
|
||||
} else if ( ILaunchVMConstants.PROP_STATE_CHANGE_DETAILS.equals(propertyName) ) {
|
||||
return properties.get(propertyName);
|
||||
|
|
|
@ -51,4 +51,5 @@ public class MessagesForLaunchVM extends NLS {
|
|||
public static String State_change_reason__Shared_lib__label;
|
||||
public static String State_change_reason__Error__label;
|
||||
public static String State_change_reason__Evaluation__label;
|
||||
public static String State_change_reason__EventBreakpoint__label;
|
||||
}
|
||||
|
|
|
@ -66,4 +66,5 @@ State_change_reason__Watchpoint__label = Watchpoint
|
|||
State_change_reason__Signal__label = Signal
|
||||
State_change_reason__Shared_lib__label = Shared Library
|
||||
State_change_reason__Error__label = Error
|
||||
State_change_reason__Evaluation__label = Evaluation
|
||||
State_change_reason__Evaluation__label = Evaluation
|
||||
State_change_reason__EventBreakpoint__label = Event Breakpoint
|
|
@ -46,7 +46,11 @@ public interface IRunControl extends IDsfService
|
|||
public interface IContainerDMContext extends IExecutionDMContext {}
|
||||
|
||||
/** Flag indicating reason context state change. */
|
||||
public enum StateChangeReason { UNKNOWN, USER_REQUEST, STEP, BREAKPOINT, EXCEPTION, CONTAINER, WATCHPOINT, SIGNAL, SHAREDLIB, ERROR, EVALUATION };
|
||||
public enum StateChangeReason {
|
||||
UNKNOWN, USER_REQUEST, STEP, BREAKPOINT, EXCEPTION, CONTAINER, WATCHPOINT, SIGNAL, SHAREDLIB, ERROR, EVALUATION,
|
||||
|
||||
/** @since 2.1 */
|
||||
EVENT_BREAKPOINT };
|
||||
|
||||
/**
|
||||
* Indicates that the given thread has suspended.
|
||||
|
|
Loading…
Add table
Reference in a new issue