From a2f1be9effcb9a0951e6a59b2d10feb0879d975d Mon Sep 17 00:00:00 2001 From: John Cortell Date: Thu, 22 Apr 2010 23:03:58 +0000 Subject: [PATCH] [280631] Thread list in Debug view not updated with non-Linux target --- .../contexts_CDT_DEBUGGER_DSFGDB.xml | 3 + .../ui/launching/GdbDebuggerPage.java | 111 +++++++++--------- .../ui/launching/LaunchUIMessages.properties | 1 + .../gdb/IGDBLaunchConfigurationConstants.java | 17 +++ .../cdt/dsf/gdb/service/GDBBackend.java | 8 ++ .../cdt/dsf/gdb/service/GDBProcesses_7_0.java | 11 ++ .../cdt/dsf/gdb/service/IGDBBackend.java | 14 +++ .../cdt/dsf/mi/service/MIProcesses.java | 26 +++- 8 files changed, 132 insertions(+), 59 deletions(-) diff --git a/doc/org.eclipse.cdt.doc.user/contexts_CDT_DEBUGGER_DSFGDB.xml b/doc/org.eclipse.cdt.doc.user/contexts_CDT_DEBUGGER_DSFGDB.xml index dab118ca0d0..fe212089711 100644 --- a/doc/org.eclipse.cdt.doc.user/contexts_CDT_DEBUGGER_DSFGDB.xml +++ b/doc/org.eclipse.cdt.doc.user/contexts_CDT_DEBUGGER_DSFGDB.xml @@ -15,6 +15,9 @@ Control the behavior of the C/C++ debugger when debugging with GDB, specifically when using a GDB (DSF) launcher. + + This checkbox controls whether the CDT debugger will ask gdb for the target program's thread list on each suspend event (breakpoint-hit, step, etc). Normally, this isn't necessary, as GDB sends notifications in realtime when a thread is created or destroyed. However, some lightweight GDB remote stubs won't send these notifications. As such, the CDT debugger doesn't find out about new or destroyed threads unless it polls gdb. Turn on this option if you are debugging such a target (typically an embedded one). + 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 bb4956d399c..817e866d161 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 @@ -17,6 +17,7 @@ import java.util.Observer; import org.eclipse.cdt.debug.ui.AbstractCDebuggerPage; import org.eclipse.cdt.dsf.gdb.IGDBLaunchConfigurationConstants; +import org.eclipse.cdt.dsf.gdb.internal.ui.GdbUIPlugin; import org.eclipse.cdt.utils.ui.controls.ControlFactory; import org.eclipse.core.runtime.CoreException; import org.eclipse.debug.core.ILaunchConfiguration; @@ -36,6 +37,7 @@ import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.TabFolder; import org.eclipse.swt.widgets.TabItem; import org.eclipse.swt.widgets.Text; +import org.eclipse.ui.PlatformUI; /** * The dynamic tab for gdb-based debugger implementations. @@ -47,6 +49,7 @@ public class GdbDebuggerPage extends AbstractCDebuggerPage implements Observer { protected Text fGDBInitText; protected Button fNonStopCheckBox; protected Button fReverseCheckBox; + protected Button fUpdateThreadlistOnSuspend; private IMILaunchConfigurationComponent fSolibBlock; private boolean fIsInitializing = false; @@ -71,6 +74,8 @@ public class GdbDebuggerPage extends AbstractCDebuggerPage implements Observer { IGDBLaunchConfigurationConstants.DEBUGGER_NON_STOP_DEFAULT); configuration.setAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_REVERSE, IGDBLaunchConfigurationConstants.DEBUGGER_REVERSE_DEFAULT); + configuration.setAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_UPDATE_THREADLIST_ON_SUSPEND, + IGDBLaunchConfigurationConstants.DEBUGGER_UPDATE_THREADLIST_ON_SUSPEND_DEFAULT); if (fSolibBlock != null) fSolibBlock.setDefaults(configuration); @@ -90,46 +95,45 @@ public class GdbDebuggerPage extends AbstractCDebuggerPage implements Observer { return valid; } + /** utility method to cut down on clutter */ + private String getStringAttr(ILaunchConfiguration config, String attributeName, String defaultValue) { + try { + return config.getAttribute(attributeName, defaultValue); + } + catch (CoreException exc) { + return defaultValue; + } + } + /** utility method to cut down on clutter */ + private boolean getBooleanAttr(ILaunchConfiguration config, String attributeName, boolean defaultValue) { + try { + return config.getAttribute(attributeName, defaultValue); + } + catch (CoreException exc) { + return defaultValue; + } + } + public void initializeFrom(ILaunchConfiguration configuration) { setInitializing(true); - 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; - - try { - gdbCommand = configuration.getAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUG_NAME, - IGDBLaunchConfigurationConstants.DEBUGGER_DEBUG_NAME_DEFAULT); - } - catch(CoreException e) { - } - try { - gdbInit = configuration.getAttribute(IGDBLaunchConfigurationConstants.ATTR_GDB_INIT, - IGDBLaunchConfigurationConstants.DEBUGGER_GDB_INIT_DEFAULT); - } - catch(CoreException e) { - } - - try { - nonStopMode = configuration.getAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_NON_STOP, - IGDBLaunchConfigurationConstants.DEBUGGER_NON_STOP_DEFAULT); - } - catch(CoreException e) { - } + String gdbCommand = getStringAttr(configuration, IGDBLaunchConfigurationConstants.ATTR_DEBUG_NAME, + IGDBLaunchConfigurationConstants.DEBUGGER_DEBUG_NAME_DEFAULT); + String gdbInit = getStringAttr(configuration, IGDBLaunchConfigurationConstants.ATTR_GDB_INIT, + IGDBLaunchConfigurationConstants.DEBUGGER_GDB_INIT_DEFAULT); + boolean nonStopMode = getBooleanAttr(configuration, IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_NON_STOP, + IGDBLaunchConfigurationConstants.DEBUGGER_NON_STOP_DEFAULT); + boolean reverseEnabled = getBooleanAttr(configuration, IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_REVERSE, + IGDBLaunchConfigurationConstants.DEBUGGER_REVERSE_DEFAULT); + boolean updateThreadsOnSuspend = getBooleanAttr(configuration, IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_UPDATE_THREADLIST_ON_SUSPEND, + IGDBLaunchConfigurationConstants.DEBUGGER_UPDATE_THREADLIST_ON_SUSPEND_DEFAULT); - try { - reverseEnabled = configuration.getAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_REVERSE, - IGDBLaunchConfigurationConstants.DEBUGGER_REVERSE_DEFAULT); - } - catch(CoreException e) { - } - if (fSolibBlock != null) fSolibBlock.initializeFrom(configuration); fGDBCommandText.setText(gdbCommand); fGDBInitText.setText(gdbInit); fNonStopCheckBox.setSelection(nonStopMode); fReverseCheckBox.setSelection(reverseEnabled); + fUpdateThreadlistOnSuspend.setSelection(updateThreadsOnSuspend); setInitializing(false); } @@ -143,6 +147,8 @@ public class GdbDebuggerPage extends AbstractCDebuggerPage implements Observer { fNonStopCheckBox.getSelection()); configuration.setAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_REVERSE, fReverseCheckBox.getSelection()); + configuration.setAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_UPDATE_THREADLIST_ON_SUSPEND, + fUpdateThreadlistOnSuspend.getSelection()); if (fSolibBlock != null) fSolibBlock.performApply(configuration); @@ -284,32 +290,15 @@ public class GdbDebuggerPage extends AbstractCDebuggerPage implements Observer { // TODO: Ideally, this field should be disabled if the back-end doesn't support non-stop debugging // TODO: Find a way to determine if non-stop is supported (i.e. find the GDB version) then grey out the check box if necessary - fNonStopCheckBox = ControlFactory.createCheckBox(subComp, LaunchUIMessages.getString("GDBDebuggerPage.nonstop_mode")); //$NON-NLS-1$ - fNonStopCheckBox.addSelectionListener( new SelectionAdapter() { - @Override - public void widgetSelected(SelectionEvent e) { - updateLaunchConfigurationDialog(); - } - }); + fNonStopCheckBox = addCheckbox(subComp, LaunchUIMessages.getString("GDBDebuggerPage.nonstop_mode")); //$NON-NLS-1$ // 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.reverse_Debugging")); //$NON-NLS-1$ - fReverseCheckBox.addSelectionListener( new SelectionAdapter() { - @Override - public void widgetSelected(SelectionEvent e) { - updateLaunchConfigurationDialog(); - } - }); + fReverseCheckBox = addCheckbox(subComp, LaunchUIMessages.getString("GDBDebuggerPage.reverse_Debugging")); //$NON-NLS-1$ - - // fit options one per line - gd = new GridData(); - gd.horizontalSpan = 3; - fNonStopCheckBox.setLayoutData(gd); - gd = new GridData(); - gd.horizontalSpan = 3; - fReverseCheckBox.setLayoutData(gd); + fUpdateThreadlistOnSuspend = addCheckbox(subComp, LaunchUIMessages.getString("GDBDebuggerPage.update_thread_list_on_suspend")); //$NON-NLS-1$ + // This checkbox needs an explanation. Attach context help to it. + PlatformUI.getWorkbench().getHelpSystem().setHelp(fUpdateThreadlistOnSuspend, GdbUIPlugin.PLUGIN_ID + ".update_threadlist_button_context"); //$NON-NLS-1$ } public void createSolibTab(TabFolder tabFolder) { @@ -323,6 +312,22 @@ public class GdbDebuggerPage extends AbstractCDebuggerPage implements Observer { ((Observable)fSolibBlock).addObserver(this); } + /** Used to add a checkbox to the tab. Each checkbox has its own line. */ + private Button addCheckbox(Composite parent, String label) { + Button button = ControlFactory.createCheckBox(parent, label); + button .addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + updateLaunchConfigurationDialog(); + } + }); + GridData gd = new GridData(); + gd.horizontalSpan = 3; + button.setLayoutData(gd); + + return button; + } + /* * (non-Javadoc) * 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 07024579a2d..88ec0e23e5e 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 @@ -23,6 +23,7 @@ GDBDebuggerPage.cmdfile_warning=(Warning: Some commands in this file may interfe GDBDebuggerPage.shared_libraries=Shared Libraries GDBDebuggerPage.nonstop_mode=Non-stop mode (Note: Requires non-stop GDB) GDBDebuggerPage.reverse_Debugging=Enable Reverse Debugging at startup (Note: Requires Reverse GDB) +GDBDebuggerPage.update_thread_list_on_suspend=Force thread list update on suspend 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 236d469b23c..5daee719166 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 @@ -81,6 +81,14 @@ public class IGDBLaunchConfigurationConstants { * @since 2.0 */ public static final String ATTR_DEBUGGER_REVERSE = GdbPlugin.PLUGIN_ID + ".REVERSE"; //$NON-NLS-1$ + + /** + * Launch configuration attribute key. Boolean value. See + * IGDBBackend.getUpdateThreadListOnSuspend() + * + * @since 3.0 + */ + public static final String ATTR_DEBUGGER_UPDATE_THREADLIST_ON_SUSPEND = GdbPlugin.PLUGIN_ID + ".UPDATE_THREADLIST_ON_SUSPEND"; //$NON-NLS-1$ /** * Launch configuration attribute value. The key is ATTR_DEBUG_NAME. @@ -114,4 +122,13 @@ public class IGDBLaunchConfigurationConstants { * @since 2.0 */ public static final boolean DEBUGGER_REVERSE_DEFAULT = false; + + /** + * Launch configuration attribute value. The key is + * ATTR_DEBUGGER_UPDATE_THREADLIST_ON_SUSPEND + * + * @since 3.0 + */ + public static final boolean DEBUGGER_UPDATE_THREADLIST_ON_SUSPEND_DEFAULT = false; + } diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBBackend.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBBackend.java index a99e67c353e..5b00180bafa 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBBackend.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBBackend.java @@ -335,6 +335,14 @@ public class GDBBackend extends AbstractDsfService implements IGDBBackend { return !fLaunchConfiguration.getAttribute(ILaunchManager.ATTR_APPEND_ENVIRONMENT_VARIABLES, true); } + /** @since 3.0 */ + public boolean getUpdateThreadListOnSuspend() throws CoreException { + return fLaunchConfiguration + .getAttribute( + IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_UPDATE_THREADLIST_ON_SUSPEND, + IGDBLaunchConfigurationConstants.DEBUGGER_UPDATE_THREADLIST_ON_SUSPEND_DEFAULT); + } + /* * Launch GDB process. * Allow subclass to override. diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBProcesses_7_0.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBProcesses_7_0.java index f62e93cc95f..a05eae80b00 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBProcesses_7_0.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBProcesses_7_0.java @@ -65,6 +65,7 @@ import org.eclipse.cdt.dsf.mi.service.command.output.MIListThreadGroupsInfo.IThr 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.IStatus; import org.eclipse.core.runtime.Status; import org.osgi.framework.BundleContext; @@ -882,6 +883,16 @@ public class GDBProcesses_7_0 extends AbstractDsfService } else { // This will happen in non-stop mode } + + // If user is debugging a gdb target that doesn't send thread + // creation events, make sure we don't use cached thread + // information. Reset the cache after every suspend. See bugzilla + // 280631 + try { + if (fBackend.getUpdateThreadListOnSuspend()) { + fThreadCommandCache.reset(e.getDMContext()); + } + } catch (CoreException exc) {} } // Event handler when a thread or threadGroup starts diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/IGDBBackend.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/IGDBBackend.java index 84edfbe44b0..595bdb4a62c 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/IGDBBackend.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/IGDBBackend.java @@ -133,4 +133,18 @@ public interface IGDBBackend extends IMIBackend { * @return true if the ongoing session is attaching to a remote target. */ public boolean getIsAttachSession(); + + /** + * Indicates whether the CDT debugger should ask gdb for the target + * program's thread list on each suspend event (breakpoint-hit, step, etc). + * Normally, this isn't necessary, as GDB sends notifications in realtime + * when a thread is created or destroyed. However, some lightweight GDB + * remote stubs won't send these notifications. As such, the CDT debugger + * doesn't find out about new or destroyed threads unless it polls gdb. The + * user will enable this behavior if he is debugging such a target + * (typically an embedded one) + * + * @since 3.0 + */ + public boolean getUpdateThreadListOnSuspend() throws CoreException; } diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/MIProcesses.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/MIProcesses.java index d5a2f27cdc2..60989e955ed 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/MIProcesses.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/MIProcesses.java @@ -33,6 +33,7 @@ import org.eclipse.cdt.dsf.debug.service.command.CommandCache; import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService; 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.command.CommandFactory; import org.eclipse.cdt.dsf.mi.service.command.output.CLIInfoThreadsInfo; import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo; @@ -40,6 +41,7 @@ import org.eclipse.cdt.dsf.mi.service.command.output.MIThreadListIdsInfo; 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.IStatus; import org.eclipse.core.runtime.Status; import org.osgi.framework.BundleContext; @@ -305,6 +307,7 @@ public class MIProcesses extends AbstractDsfService implements IMIProcesses, ICa private ICommandControlService fCommandControl; private CommandCache fContainerCommandCache; private CommandFactory fCommandFactory; + private IGDBBackend fGdbBackend; private static final String FAKE_THREAD_ID = "0"; //$NON-NLS-1$ // The unique id should be an empty string so that the views know not to display the fake id @@ -350,6 +353,8 @@ public class MIProcesses extends AbstractDsfService implements IMIProcesses, ICa fCommandFactory = getServicesTracker().getService(IMICommandControl.class).getCommandFactory(); + fGdbBackend = getServicesTracker().getService(IGDBBackend.class); + // This cache stores the result of a command when received; also, this cache // is manipulated when receiving events. Currently, events are received after // three scheduling of the executor, while command results after only one. This @@ -638,12 +643,21 @@ public class MIProcesses extends AbstractDsfService implements IMIProcesses, ICa */ @DsfServiceEventHandler public void eventDispatched(ISuspendedDMEvent e) { - if (e instanceof IContainerSuspendedDMEvent) { - // This will happen in all-stop mode - fContainerCommandCache.setContextAvailable(e.getDMContext(), true); - } else { - // This will happen in non-stop mode - } + // This assert may turn out to be overzealous. Refer to + // https://bugs.eclipse.org/bugs/show_bug.cgi?id=280631#c26 + assert e instanceof IContainerSuspendedDMEvent : "Unexpected type of suspended event: " + e.getClass().toString(); //$NON-NLS-1$ + + fContainerCommandCache.setContextAvailable(e.getDMContext(), true); + + // If user is debugging a gdb target that doesn't send thread + // creation events, make sure we don't use cached thread + // information. Reset the cache after every suspend. See bugzilla + // 280631 + try { + if (fGdbBackend.getUpdateThreadListOnSuspend()) { + fContainerCommandCache.reset(e.getDMContext()); + } + } catch (CoreException exc) {} } /**