diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/viewmodel/launch/ThreadVMNode.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/viewmodel/launch/ThreadVMNode.java index 4106f358438..cafdab38d71 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/viewmodel/launch/ThreadVMNode.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/viewmodel/launch/ThreadVMNode.java @@ -76,7 +76,9 @@ public class ThreadVMNode extends AbstractThreadVMNode IGdbLaunchVMConstants.PROP_OS_ID, ILaunchVMConstants.PROP_IS_SUSPENDED, ExecutionContextLabelText.PROP_STATE_CHANGE_REASON_KNOWN, - ILaunchVMConstants.PROP_STATE_CHANGE_REASON }), + ILaunchVMConstants.PROP_STATE_CHANGE_REASON, + ExecutionContextLabelText.PROP_STATE_CHANGE_DETAILS_KNOWN, + ILaunchVMConstants.PROP_STATE_CHANGE_DETAILS}), new LabelText(MessagesForGdbLaunchVM.ThreadVMNode_No_columns__Error__label, new String[0]), new LabelImage(DebugUITools.getImageDescriptor(IDebugUIConstants.IMG_OBJS_THREAD_RUNNING)) { { setPropertyNames(new String[] { ILaunchVMConstants.PROP_IS_SUSPENDED }); } diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/viewmodel/launch/messages.properties b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/viewmodel/launch/messages.properties index 43207ee802c..46a0088f153 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/viewmodel/launch/messages.properties +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/viewmodel/launch/messages.properties @@ -18,7 +18,9 @@ # {6} - 0=running/1=suspended # {7} - state change reason available, 0=not available/1=available # {8} - state change reason -ThreadVMNode_No_columns__text_format={0,choice,0#Thread|1#{1}}{2,choice,0#|1# [{3}]}{4,choice,0#|1# {5}} ({6,choice,0#Running|1#Suspended}{7,choice,0#|1# : {8}}) +# {9} - state change details available, 0=not available/1=available +# {10}- state change details +ThreadVMNode_No_columns__text_format={0,choice,0#Thread|1#{1}}{2,choice,0#|1# [{3}]}{4,choice,0#|1# {5}} ({6,choice,0#Running|1#Suspended}{7,choice,0#|1# : {8}}{9,choice,0#|1# : {10}}) ThreadVMNode_No_columns__Error__label= diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBRunControl_7_0_NS.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBRunControl_7_0_NS.java index 8dd61210eb7..08d70369be1 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBRunControl_7_0_NS.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBRunControl_7_0_NS.java @@ -93,12 +93,16 @@ import org.osgi.framework.BundleContext; public class GDBRunControl_7_0_NS extends AbstractDsfService implements IMIRunControl, ICachingService { @Immutable - private static class ExecutionData implements IExecutionDMData { + private static class ExecutionData implements IExecutionDMData2 { private final StateChangeReason fReason; - ExecutionData(StateChangeReason reason) { + private final String fDetails; + + ExecutionData(StateChangeReason reason, String details) { fReason = reason; + fDetails = details; } public StateChangeReason getStateChangeReason() { return fReason; } + public String getDetails() { return fDetails; } } /** @@ -153,6 +157,23 @@ public class GDBRunControl_7_0_NS extends AbstractDsfService implements IMIRunCo return StateChangeReason.USER_REQUEST; } } + + public String getDetails() { + MIStoppedEvent event = getMIEvent(); + if (event instanceof MICatchpointHitEvent) { // must precede MIBreakpointHitEvent + return ((MICatchpointHitEvent)event).getReason(); + } else if (event instanceof MISharedLibEvent) { + return ((MISharedLibEvent)event).getLibrary(); + } else if (event instanceof MISignalEvent) { + return ((MISignalEvent)event).getName() + ':' + ((MISignalEvent)event).getMeaning(); + } else if (event instanceof MIWatchpointTriggerEvent) { + return ((MIWatchpointTriggerEvent)event).getExpression(); + } else if (event instanceof MIErrorEvent) { + return ((MIErrorEvent)event).getMessage(); + } + + return null; + } } /** @@ -227,7 +248,19 @@ public class GDBRunControl_7_0_NS extends AbstractDsfService implements IMIRunCo boolean fSuspended = false; boolean fResumePending = false; boolean fStepping = false; + + /** + * What caused the state change. E.g., a signal was thrown. + */ StateChangeReason fStateChangeReason; + + /** + * Further detail on what caused the state change. E.g., the specific signal + * that was throw was a SIGINT. The exact string comes from gdb in the mi + * event. May be null, as not all types of state change have additional + * detail of interest. + */ + String fStateChangeDetails; } private static class RunToLineActiveOperation { @@ -783,7 +816,7 @@ public class GDBRunControl_7_0_NS extends AbstractDsfService implements IMIRunCo } if (dmc instanceof IMIExecutionDMContext) { - rm.setData(new ExecutionData(threadState.fSuspended ? threadState.fStateChangeReason : null)); + rm.setData(new ExecutionData(threadState.fSuspended ? threadState.fStateChangeReason : null, threadState.fStateChangeDetails)); } else { rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, "Given context: " + dmc + " is not a recognized execution context.", null)); //$NON-NLS-1$ //$NON-NLS-2$ @@ -817,6 +850,7 @@ public class GDBRunControl_7_0_NS extends AbstractDsfService implements IMIRunCo threadState.fSuspended = false; threadState.fResumePending = false; threadState.fStateChangeReason = reason; + threadState.fStateChangeDetails = null; // we have no details of interest for a resume threadState.fStepping = isStepping; } @@ -831,6 +865,7 @@ public class GDBRunControl_7_0_NS extends AbstractDsfService implements IMIRunCo threadState.fResumePending = false; threadState.fStepping = false; threadState.fStateChangeReason = reason; + threadState.fStateChangeDetails = event.getDetails(); } diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/MIRunControl.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/MIRunControl.java index b672b40f09e..f849f6eba01 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/MIRunControl.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/MIRunControl.java @@ -130,12 +130,15 @@ public class MIRunControl extends AbstractDsfService implements IMIRunControl, I } @Immutable - private static class ExecutionData implements IExecutionDMData { + private static class ExecutionData implements IExecutionDMData2 { private final StateChangeReason fReason; - ExecutionData(StateChangeReason reason) { + private final String fDetails; + ExecutionData(StateChangeReason reason, String details) { fReason = reason; + fDetails = details; } public StateChangeReason getStateChangeReason() { return fReason; } + public String getDetails() { return fDetails; } } /** @@ -190,6 +193,26 @@ public class MIRunControl extends AbstractDsfService implements IMIRunControl, I return StateChangeReason.USER_REQUEST; } } + + /** + * @since 3.0 + */ + public String getDetails() { + MIStoppedEvent event = getMIEvent(); + if (event instanceof MICatchpointHitEvent) { // must precede MIBreakpointHitEvent + return ((MICatchpointHitEvent)event).getReason(); + } else if (event instanceof MISharedLibEvent) { + return ((MISharedLibEvent)event).getLibrary(); + } else if (event instanceof MISignalEvent) { + return ((MISignalEvent)event).getName() + ':' + ((MISignalEvent)event).getMeaning(); + } else if (event instanceof MIWatchpointTriggerEvent) { + return ((MIWatchpointTriggerEvent)event).getExpression(); + } else if (event instanceof MIErrorEvent) { + return ((MIErrorEvent)event).getMessage(); + } + + return null; + } } /** @@ -324,7 +347,20 @@ public class MIRunControl extends AbstractDsfService implements IMIRunControl, I private boolean fStepping = false; private boolean fTerminated = false; + + /** + * What caused the state change. E.g., a signal was thrown. + */ private StateChangeReason fStateChangeReason; + + /** + * Further detail on what caused the state change. E.g., the specific signal + * that was throw was a SIGINT. The exact string comes from gdb in the mi + * event. May be null, as not all types of state change have additional + * detail of interest. + */ + private String fStateChangeDetails; + private IExecutionDMContext fStateChangeTriggeringContext; /** * Indicates that the next MIRunning event should be silenced. @@ -526,6 +562,7 @@ public class MIRunControl extends AbstractDsfService implements IMIRunControl, I fSuspended = false; fResumePending = false; fStateChangeReason = e.getReason(); + fStateChangeDetails = null; // we have no details of interest for a resume fMICommandCache.setContextAvailable(e.getDMContext(), false); //fStateChangeTriggeringContext = e.getTriggeringContext(); if (e.getReason().equals(StateChangeReason.STEP)) { @@ -544,6 +581,7 @@ public class MIRunControl extends AbstractDsfService implements IMIRunControl, I fMICommandCache.setContextAvailable(e.getDMContext(), true); fMICommandCache.reset(); fStateChangeReason = e.getReason(); + fStateChangeDetails = e.getDetails(); fStateChangeTriggeringContext = e.getTriggeringContexts().length != 0 ? e.getTriggeringContexts()[0] : null; fSuspended = true; @@ -793,10 +831,12 @@ public class MIRunControl extends AbstractDsfService implements IMIRunControl, I public void getExecutionData(IExecutionDMContext dmc, DataRequestMonitor rm){ if (dmc instanceof IContainerDMContext) { - rm.setData( new ExecutionData(fStateChangeReason) ); + rm.setData( new ExecutionData(fStateChangeReason, fStateChangeDetails) ); } else if (dmc instanceof IMIExecutionDMContext) { - StateChangeReason reason = dmc.equals(fStateChangeTriggeringContext) ? fStateChangeReason : StateChangeReason.CONTAINER; - rm.setData(new ExecutionData(reason)); + boolean thisThreadCausedStateChange = dmc.equals(fStateChangeTriggeringContext); + StateChangeReason reason = thisThreadCausedStateChange ? fStateChangeReason : StateChangeReason.CONTAINER; + String details = thisThreadCausedStateChange ? fStateChangeDetails : null; + rm.setData(new ExecutionData(reason, details)); } else { rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, "Given context: " + dmc + " is not an execution context.", null)); //$NON-NLS-1$ //$NON-NLS-2$ } diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/events/MIBreakpointHitEvent.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/events/MIBreakpointHitEvent.java index 6bdc9083073..e83ee142b9f 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/events/MIBreakpointHitEvent.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/events/MIBreakpointHitEvent.java @@ -68,12 +68,22 @@ 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); - } - } + // We might be here because of a catchpoint hit; in gdb >= 7.0, + // catchpoints are reported as breakpoints. Unfortunately, there's + // nothing in the stopped event indicating it's a catchpoint, and unlike + // gdb < 7.0, there are no stream records that reveal it's a catchpoint. + // The only way to determine it's a catchpoint is to map the gdb breakpoint + // number back to the CBreakpoint (platform) instance, and that *will* reveal + // whether it's a catchpoint or not, and even which kind of catchpoint + // TODO: + /* + CBreakpoint cbkpt = FromSomewhere.getCBreakpointForGdbBreakpoint(bkptno); <== this method doesn't exist yet + if (cbkpt instanceof CEventBreakpoint) { + String eventTypeID = ((CEventBreakpoint)cbkpt).getEventType(); + String gdbKeyword = GdbCatchpoints.eventToGdbCatchpointKeyword(eventTypeID) + return MICatchpointHitEvent.parse(stoppedEvent.getDMContext(), token, results, gdbKeyword); + } + */ return new MIBreakpointHitEvent(stoppedEvent.getDMContext(), token, results, stoppedEvent.getFrame(), bkptno); } diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/events/MICatchpointHitEvent.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/events/MICatchpointHitEvent.java index 156a062cd03..7f07a31340e 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/events/MICatchpointHitEvent.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/events/MICatchpointHitEvent.java @@ -12,9 +12,25 @@ import org.eclipse.cdt.dsf.mi.service.command.output.MIStreamRecord; */ public class MICatchpointHitEvent extends MIBreakpointHitEvent { + /** + * See {@link #getReason()} + */ + private String fReason; + protected MICatchpointHitEvent(IExecutionDMContext ctx, int token, - MIResult[] results, MIFrame frame, int bkptno) { + MIResult[] results, MIFrame frame, int bkptno, String reason) { super(ctx, token, results, frame, bkptno); + fReason = reason; + } + + /** + * Returns the event the cachpoint caught. E.g., a C++ exception was thrown. + * This string comes either from gdb when the catchpoint is hit, or it's the + * gdb catchpoint keyword ('catch', 'throw', 'fork', etc) that was used to + * set the catchpoint (to be done for gdb >= 7.0) + */ + public String getReason() { + return fReason; } /** @@ -31,9 +47,27 @@ public class MICatchpointHitEvent extends MIBreakpointHitEvent { StringTokenizer tokenizer = new StringTokenizer(streamRecord.getString()); tokenizer.nextToken(); // "Catchpoint" try { - int bkptNumber = Integer.parseInt(tokenizer.nextToken()); // "1" (e.g.,) + int bkptNumber = Integer.parseInt(tokenizer.nextToken()); // "1" + StringBuilder reason = new StringBuilder(); + boolean first = true; + while (tokenizer.hasMoreElements()) { + if (!first) { + reason.append(" "); //$NON-NLS-1$ ok; technically, the delim could be any whitespace, but we know it's s a space char + } + reason.append(tokenizer.nextElement()); + first = false; + } + + // remove the parentheses + if (reason.charAt(0) == '(') { + reason.deleteCharAt(0); + } + if (reason.charAt(reason.length()-1) == ')') { + reason.deleteCharAt(reason.length()-1); + } + MIStoppedEvent stoppedEvent = MIStoppedEvent.parse(dmc, token, results); - return new MICatchpointHitEvent(stoppedEvent.getDMContext(), token, results, stoppedEvent.getFrame(), bkptNumber); + return new MICatchpointHitEvent(stoppedEvent.getDMContext(), token, results, stoppedEvent.getFrame(), bkptNumber, reason.toString()); } catch (NumberFormatException exc) { assert false : "unexpected catchpoint stream record format: " + streamRecord.getString(); //$NON-NLS-1$ diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/events/MISharedLibEvent.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/events/MISharedLibEvent.java index beb309ec7fd..272dd970554 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/events/MISharedLibEvent.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/events/MISharedLibEvent.java @@ -23,13 +23,28 @@ import org.eclipse.cdt.dsf.mi.service.command.output.MIResult; @Immutable public class MISharedLibEvent extends MIStoppedEvent { - protected MISharedLibEvent(IExecutionDMContext ctx, int token, MIResult[] results, MIFrame frame) { + /** See {@link #getLibrary()} */ + private String fLibrary; + + /** + * @since 3.0 + */ + protected MISharedLibEvent(IExecutionDMContext ctx, int token, MIResult[] results, MIFrame frame, String library) { super(ctx, token, results, frame); + fLibrary = library; + } + + /** The library that was loaded, as reported by gdb. + * @since 3.0*/ + public String getLibrary() { + return fLibrary; } - public static MIStoppedEvent parse(IExecutionDMContext dmc, int token, MIResult[] results) - { + /** + * @since 3.0 + */ + public static MIStoppedEvent parse(IExecutionDMContext dmc, int token, MIResult[] results, String library) { MIStoppedEvent stoppedEvent = MIStoppedEvent.parse(dmc, token, results); - return new MISharedLibEvent(stoppedEvent.getDMContext(), token, results, stoppedEvent.getFrame()); + return new MISharedLibEvent(stoppedEvent.getDMContext(), token, results, stoppedEvent.getFrame(), library); } }