From ea1399cf078a8a815de7971b979a92f1f56491c3 Mon Sep 17 00:00:00 2001 From: Marc Khouzam Date: Mon, 9 Feb 2009 21:34:16 +0000 Subject: [PATCH] [258284] This patch allows the user to enable reverse debugging from the launch. I got it all to work except for one case: if the user turns off StopOnMain, but actually has a real breakpoint on main, then that breakpoint will be ignored when having Reverse on in the launch. --- .../ui/launching/GdbDebuggerPage.java | 33 ++- .../ui/launching/LaunchUIMessages.properties | 1 + .../gdb/IGDBLaunchConfigurationConstants.java | 12 + .../dsf/gdb/service/GDBRunControl_7_0.java | 4 +- .../gdb/service/command/GDBControl_7_0.java | 259 +++++++++++++----- 5 files changed, 239 insertions(+), 70 deletions(-) diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/launching/GdbDebuggerPage.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/launching/GdbDebuggerPage.java index 8a6a1478ad8..24fcac764d8 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/launching/GdbDebuggerPage.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/launching/GdbDebuggerPage.java @@ -46,8 +46,10 @@ public class GdbDebuggerPage extends AbstractCDebuggerPage implements Observer { protected Text fGDBCommandText; protected Text fGDBInitText; protected Button fNonStopCheckBox; + protected Button fReverseCheckBox; protected Button fVerboseModeButton; + private IMILaunchConfigurationComponent fSolibBlock; private boolean fIsInitializing = false; @@ -69,6 +71,8 @@ public class GdbDebuggerPage extends AbstractCDebuggerPage implements Observer { IGDBLaunchConfigurationConstants.DEBUGGER_GDB_INIT_DEFAULT); configuration.setAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_NON_STOP, IGDBLaunchConfigurationConstants.DEBUGGER_NON_STOP_DEFAULT); + configuration.setAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_REVERSE, + IGDBLaunchConfigurationConstants.DEBUGGER_REVERSE_DEFAULT); configuration.setAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_VERBOSE_MODE, IGDBLaunchConfigurationConstants.DEBUGGER_VERBOSE_MODE_DEFAULT); @@ -95,6 +99,8 @@ public class GdbDebuggerPage extends AbstractCDebuggerPage implements Observer { String gdbCommand = IGDBLaunchConfigurationConstants.DEBUGGER_DEBUG_NAME_DEFAULT; String gdbInit = IGDBLaunchConfigurationConstants.DEBUGGER_GDB_INIT_DEFAULT; boolean nonStopMode = IGDBLaunchConfigurationConstants.DEBUGGER_NON_STOP_DEFAULT; + boolean reverseEnabled = IGDBLaunchConfigurationConstants.DEBUGGER_REVERSE_DEFAULT; + boolean verboseMode = IGDBLaunchConfigurationConstants.DEBUGGER_VERBOSE_MODE_DEFAULT; try { @@ -116,6 +122,14 @@ public class GdbDebuggerPage extends AbstractCDebuggerPage implements Observer { } catch(CoreException e) { } + + try { + reverseEnabled = configuration.getAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_REVERSE, + IGDBLaunchConfigurationConstants.DEBUGGER_REVERSE_DEFAULT); + } + catch(CoreException e) { + } + try { verboseMode = configuration.getAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_VERBOSE_MODE, IGDBLaunchConfigurationConstants.DEBUGGER_VERBOSE_MODE_DEFAULT ); @@ -128,6 +142,7 @@ public class GdbDebuggerPage extends AbstractCDebuggerPage implements Observer { fGDBCommandText.setText(gdbCommand); fGDBInitText.setText(gdbInit); fNonStopCheckBox.setSelection(nonStopMode); + fReverseCheckBox.setSelection(reverseEnabled); fVerboseModeButton.setSelection(verboseMode); setInitializing(false); @@ -140,6 +155,8 @@ public class GdbDebuggerPage extends AbstractCDebuggerPage implements Observer { fGDBInitText.getText().trim()); configuration.setAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_NON_STOP, fNonStopCheckBox.getSelection()); + configuration.setAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_REVERSE, + fReverseCheckBox.getSelection()); configuration.setAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_VERBOSE_MODE, fVerboseModeButton.getSelection() ); @@ -291,6 +308,16 @@ public class GdbDebuggerPage extends AbstractCDebuggerPage implements Observer { } }); + // TODO: Ideally, this field should be disabled if the back-end doesn't support reverse debugging + // TODO: Find a way to determine if reverse is supported (i.e. find the GDB version) then grey out the check box if necessary + fReverseCheckBox = ControlFactory.createCheckBox(subComp, LaunchUIMessages.getString("GDBDebuggerPage.14")); //$NON-NLS-1$ + fReverseCheckBox.addSelectionListener( new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + updateLaunchConfigurationDialog(); + } + }); + fVerboseModeButton = ControlFactory.createCheckBox( subComp, LaunchUIMessages.getString( "StandardGDBDebuggerPage.13" ) ); //$NON-NLS-1$ fVerboseModeButton.addSelectionListener(new SelectionAdapter() { @Override @@ -312,8 +339,10 @@ public class GdbDebuggerPage extends AbstractCDebuggerPage implements Observer { fNonStopCheckBox.setLayoutData(gd); gd = new GridData(); gd.horizontalSpan = 3; - fVerboseModeButton.setLayoutData(gd); - + fReverseCheckBox.setLayoutData(gd); + gd = new GridData(); + gd.horizontalSpan = 3; + fVerboseModeButton.setLayoutData(gd); // Grayed out until bug 249227 is resolved // fVerboseModeButton.setVisible(false); diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/launching/LaunchUIMessages.properties b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/launching/LaunchUIMessages.properties index d317535bfd3..c86106a8bad 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/launching/LaunchUIMessages.properties +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/launching/LaunchUIMessages.properties @@ -25,6 +25,7 @@ GDBDebuggerPage.10=Shared Libraries GDBDebuggerPage.11=Protocol: GDBDebuggerPage.12=Default GDBDebuggerPage.13=Non-stop mode (Note: Requires non-stop GDB) +GDBDebuggerPage.14=Enable Reverse Debugging at startup (Note: Requires Reverse GDB) StandardGDBDebuggerPage.0=Debugger executable must be specified. StandardGDBDebuggerPage.1=GDB Debugger Options StandardGDBDebuggerPage.2=Main diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/IGDBLaunchConfigurationConstants.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/IGDBLaunchConfigurationConstants.java index ad9cbb8ec53..43b4a9b4f7c 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/IGDBLaunchConfigurationConstants.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/IGDBLaunchConfigurationConstants.java @@ -82,6 +82,12 @@ public class IGDBLaunchConfigurationConstants { */ public static final String ATTR_DEBUGGER_VERBOSE_MODE = GdbPlugin.PLUGIN_ID + ".verboseMode"; //$NON-NLS-1$ + /** + * Launch configuration attribute key. Boolean value to enable reverse debugging at launch time. + * @since 2.0 + */ + public static final String ATTR_DEBUGGER_REVERSE = GdbPlugin.PLUGIN_ID + ".REVERSE"; //$NON-NLS-1$ + /** * Launch configuration attribute value. The key is ATTR_DEBUG_NAME. */ @@ -114,4 +120,10 @@ public class IGDBLaunchConfigurationConstants { * @since 1.1 */ public static final boolean DEBUGGER_VERBOSE_MODE_DEFAULT = false; + + /** + * Launch configuration attribute value. The key is ATTR_DEBUGGER_REVERSE. + * @since 2.0 + */ + public static final boolean DEBUGGER_REVERSE_DEFAULT = false; } diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBRunControl_7_0.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBRunControl_7_0.java index 77dd18a54f0..aedfbaf8455 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBRunControl_7_0.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBRunControl_7_0.java @@ -184,7 +184,7 @@ public class GDBRunControl_7_0 extends MIRunControl implements IReverseRunContro public void handleSuccess() { getConnection().queueCommand(finalcmd, new DataRequestMonitor(getExecutor(), rm) { @Override - public void handleSuccess() { + public void handleCompleted() { getConnection().queueCommand( new RawCommand(finaldmc, "set exec-direction forward"), new DataRequestMonitor(getExecutor(), rm)); @@ -266,7 +266,7 @@ public class GDBRunControl_7_0 extends MIRunControl implements IReverseRunContro public void handleSuccess() { getConnection().queueCommand(finalcmd, new DataRequestMonitor(getExecutor(), rm) { @Override - public void handleSuccess() { + public void handleCompleted() { getConnection().queueCommand( new RawCommand(finaldmc, "set exec-direction forward"), new DataRequestMonitor(getExecutor(), rm)); diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/command/GDBControl_7_0.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/command/GDBControl_7_0.java index dae8fac123a..cdd9d41897f 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/command/GDBControl_7_0.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/command/GDBControl_7_0.java @@ -28,10 +28,12 @@ import org.eclipse.cdt.dsf.debug.service.IProcesses.IProcessDMContext; import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext; import org.eclipse.cdt.dsf.debug.service.command.ICommandControl; import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService; +import org.eclipse.cdt.dsf.gdb.IGDBLaunchConfigurationConstants; import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin; import org.eclipse.cdt.dsf.gdb.launching.GdbLaunch; import org.eclipse.cdt.dsf.gdb.service.GDBRunControl_7_0; import org.eclipse.cdt.dsf.gdb.service.IGDBBackend; +import org.eclipse.cdt.dsf.gdb.service.IReverseRunControl; import org.eclipse.cdt.dsf.gdb.service.SessionType; import org.eclipse.cdt.dsf.mi.service.IMIBackend; import org.eclipse.cdt.dsf.mi.service.IMIProcesses; @@ -51,9 +53,9 @@ import org.eclipse.cdt.dsf.mi.service.command.commands.MIExecRun; import org.eclipse.cdt.dsf.mi.service.command.commands.MIGDBExit; import org.eclipse.cdt.dsf.mi.service.command.commands.MIInferiorTTYSet; import org.eclipse.cdt.dsf.mi.service.command.output.MIBreakInsertInfo; +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.DsfServiceEventHandler; -import org.eclipse.cdt.dsf.service.DsfServicesTracker; import org.eclipse.cdt.dsf.service.DsfSession; import org.eclipse.cdt.utils.pty.PTY; import org.eclipse.core.runtime.CoreException; @@ -277,77 +279,202 @@ public class GDBControl_7_0 extends AbstractMIControl implements IGDBControl { /** * Insert breakpoint at entry if set, and start or restart the program. * Note that restart does not apply to remote or attach sessions. + * + * If we want to enable Reverse debugging from the start of the program we do the following: + * attachSession => enable reverse + * else => set temp bp on main, run, enable reverse, continue if bp on main was not requested by user */ - protected void startOrRestart(final GdbLaunch launch, boolean restart, final RequestMonitor requestMonitor) { - if (fMIBackend.getIsAttachSession()) { - // When attaching to a running process, we do not need to set a breakpoint or - // start the program; it is left up to the user. - requestMonitor.done(); - return; - } - - DsfServicesTracker servicesTracker = new DsfServicesTracker(GdbPlugin.getBundleContext(), getSession().getId()); - IMIProcesses procService = servicesTracker.getService(IMIProcesses.class); - servicesTracker.dispose(); - IProcessDMContext procDmc = procService.createProcessContext(fControlDmc, MIProcesses.UNIQUE_GROUP_ID); - final IContainerDMContext containerDmc = procService.createContainerContext(procDmc, MIProcesses.UNIQUE_GROUP_ID); - - final MICommand execCommand; - if (fMIBackend.getSessionType() == SessionType.REMOTE) { - // When doing remote debugging, we use -exec-continue instead of -exec-run - execCommand = new MIExecContinue(containerDmc); - } else { - execCommand = new MIExecRun(containerDmc, new String[0]); - } - - boolean stopInMain = false; + protected void startOrRestart(final GdbLaunch launch, boolean restart, RequestMonitor requestMonitor) { + boolean tmpReverseEnabled = IGDBLaunchConfigurationConstants.DEBUGGER_REVERSE_DEFAULT; try { - stopInMain = launch.getLaunchConfiguration().getAttribute( ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_STOP_AT_MAIN, false ); + tmpReverseEnabled = launch.getLaunchConfiguration().getAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_REVERSE, + IGDBLaunchConfigurationConstants.DEBUGGER_REVERSE_DEFAULT); } catch (CoreException e) { - requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Cannot retrieve stop at entry point boolean", e)); //$NON-NLS-1$ - requestMonitor.done(); - return; } + final boolean reverseEnabled = tmpReverseEnabled; + + if (fMIBackend.getIsAttachSession()) { + // Restart does not apply to attach sessions. + // + // When attaching to a running process, we do not need to set a breakpoint or + // start the program; it is left up to the user. + // We only need to turn on Reverse Debugging if requested. + if (reverseEnabled) { + IReverseRunControl reverseService = getServicesTracker().getService(IReverseRunControl.class); + if (reverseService != null) { + reverseService.enableReverseMode(fControlDmc, true, requestMonitor); + return; + } + } + requestMonitor.done(); + return; + } - final DataRequestMonitor execMonitor = new DataRequestMonitor(getExecutor(), requestMonitor) { - @Override - public void handleSuccess() { - DsfServicesTracker servicesTracker = new DsfServicesTracker(GdbPlugin.getBundleContext(), getSession().getId()); - GDBRunControl_7_0 reverseService = servicesTracker.getService(GDBRunControl_7_0.class); - servicesTracker.dispose(); + // When it is not an attach session, it gets a little more complicated + // so let's use a sequence. + getExecutor().execute(new Sequence(getExecutor(), requestMonitor) { + IContainerDMContext fContainerDmc; + MICommand fExecCommand; + String fUserStopSymbol = null; + + MIBreakpoint fUserBreakpoint = null; + boolean fUserBreakpointIsOnMain = false; + + Step[] fSteps = new Step[] { + /* + * Figure out if we should use 'exec-continue' or '-exec-run'. + */ + new Step() { + @Override + public void execute(RequestMonitor rm) { + IMIProcesses procService = getServicesTracker().getService(IMIProcesses.class); + IProcessDMContext procDmc = procService.createProcessContext(fControlDmc, MIProcesses.UNIQUE_GROUP_ID); + fContainerDmc = procService.createContainerContext(procDmc, MIProcesses.UNIQUE_GROUP_ID); - if (reverseService != null) { - // When starting or restarting a program, reverse mode is automatically disabled - reverseService.setReverseModeEnabled(false); - } - requestMonitor.done(); - } - }; - - if (!stopInMain) { - // Just start the program. - queueCommand(execCommand, execMonitor); - } else { - String stopSymbol = null; - try { - stopSymbol = launch.getLaunchConfiguration().getAttribute( ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_STOP_AT_MAIN_SYMBOL, ICDTLaunchConfigurationConstants.DEBUGGER_STOP_AT_MAIN_SYMBOL_DEFAULT ); - } catch (CoreException e) { - requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, DebugException.CONFIGURATION_INVALID, "Cannot retrieve the entry point symbol", e)); //$NON-NLS-1$ - requestMonitor.done(); - return; - } - - // Insert a breakpoint at the requested stop symbol. - queueCommand( - new MIBreakInsert(fControlDmc, true, false, null, 0, stopSymbol, 0), - new DataRequestMonitor(getExecutor(), requestMonitor) { - @Override - protected void handleSuccess() { - // After the break-insert is done, execute the -exec-run or -exec-continue command. - queueCommand(execCommand, execMonitor); - } - }); - } + if (fMIBackend.getSessionType() == SessionType.REMOTE) { + // Restart does not apply to remote sessions + // + // When doing remote debugging, we use -exec-continue instead of -exec-run + fExecCommand = new MIExecContinue(fContainerDmc); + } else { + fExecCommand = new MIExecRun(fContainerDmc, new String[0]); + } + rm.done(); + }}, + /* + * If the user requested a 'stopOnMain', let's set the temporary breakpoint + * where the user specified. + */ + new Step() { + @Override + public void execute(final RequestMonitor rm) { + boolean userRequestedStop = false; + try { + userRequestedStop = launch.getLaunchConfiguration().getAttribute( + ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_STOP_AT_MAIN, + false); + } catch (CoreException e) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Cannot retrieve stop at entry point boolean", e)); //$NON-NLS-1$ + rm.done(); + return; + } + + if (userRequestedStop) { + try { + fUserStopSymbol = launch.getLaunchConfiguration().getAttribute( + ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_STOP_AT_MAIN_SYMBOL, + ICDTLaunchConfigurationConstants.DEBUGGER_STOP_AT_MAIN_SYMBOL_DEFAULT); + } catch (CoreException e) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, DebugException.CONFIGURATION_INVALID, "Cannot retrieve the entry point symbol", e)); //$NON-NLS-1$ + rm.done(); + return; + } + + queueCommand(new MIBreakInsert(fControlDmc, true, false, null, 0, fUserStopSymbol, 0), + new DataRequestMonitor(getExecutor(), rm) { + @Override + public void handleSuccess() { + if (getData() != null) { + MIBreakpoint[] breakpoints = getData().getMIBreakpoints(); + if (breakpoints.length > 0) { + fUserBreakpoint = breakpoints[0]; + } + } + rm.done(); + } + }); + } else { + rm.done(); + } + }}, + /* + * If reverse debugging, set a breakpoint on main to be able to enable reverse + * as early as possible. + * If the user has requested a stop at the same point, we could skip this breakpoint + * however, we have to first set it to find out! So, we just leave it. + */ + new Step() { + @Override + public void execute(final RequestMonitor rm) { + if (reverseEnabled) { + queueCommand(new MIBreakInsert(fControlDmc, true, false, null, 0, + ICDTLaunchConfigurationConstants.DEBUGGER_STOP_AT_MAIN_SYMBOL_DEFAULT, 0), + new DataRequestMonitor(getExecutor(), rm) { + @Override + public void handleSuccess() { + if (getData() != null) { + MIBreakpoint[] breakpoints = getData().getMIBreakpoints(); + if (breakpoints.length > 0 && fUserBreakpoint != null) { + fUserBreakpointIsOnMain = breakpoints[0].getAddress().equals(fUserBreakpoint.getAddress()); + } + } + rm.done(); + } + }); + } else { + rm.done(); + } + }}, + /* + * Now, run the program. + */ + new Step() { + @Override + public void execute(RequestMonitor rm) { + queueCommand(fExecCommand, new DataRequestMonitor(getExecutor(), rm)); + }}, + /* + * In case of a restart, reverse debugging should be marked as off here because + * GDB will have turned it off. We may turn it back on after. + */ + new Step() { + @Override + public void execute(RequestMonitor rm) { + // Although it only makes sense for a restart, it doesn't hurt + // do to it all the time. + GDBRunControl_7_0 reverseService = getServicesTracker().getService(GDBRunControl_7_0.class); + if (reverseService != null) { + reverseService.setReverseModeEnabled(false); + } + rm.done(); + }}, + /* + * Since we have started the program, we can turn on reverse debugging if needed + */ + new Step() { + @Override + public void execute(RequestMonitor rm) { + if (reverseEnabled) { + IReverseRunControl reverseService = getServicesTracker().getService(IReverseRunControl.class); + if (reverseService != null) { + reverseService.enableReverseMode(fControlDmc, true, rm); + return; + } + } + rm.done(); + }}, + /* + * Finally, if we are enabling reverse, and the userSymbolStop is not on main, + * we should do a continue because we are currently stopped on main but that + * is not what the user requested + */ + new Step() { + @Override + public void execute(RequestMonitor rm) { + if (reverseEnabled && !fUserBreakpointIsOnMain) { + queueCommand(new MIExecContinue(fContainerDmc), + new DataRequestMonitor(getExecutor(), rm)); + } else { + rm.done(); + } + }}, + }; + + @Override + public Step[] getSteps() { + return fSteps; + } + }); } /**