mirror of
https://github.com/eclipse-cdt/cdt
synced 2025-04-29 19:45:01 +02:00
[280631] Thread list in Debug view not updated with non-Linux target
This commit is contained in:
parent
e26d82f4d5
commit
a2f1be9eff
8 changed files with 132 additions and 59 deletions
|
@ -15,6 +15,9 @@
|
|||
<description>Control the behavior of the C/C++ debugger when debugging with GDB, specifically when using a GDB (DSF) launcher.</description>
|
||||
<topic href="reference/cdt_u_dsfgdb.htm" label="GDB Preferences"/>
|
||||
</context>
|
||||
<context id="update_threadlist_button_context" title="About this checkbox">
|
||||
<description>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).</description>
|
||||
</context>
|
||||
|
||||
|
||||
</contexts>
|
||||
|
|
|
@ -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)
|
||||
*
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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) {}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Add table
Reference in a new issue