From cfd1f8213c3ea8fd699a5b24fa2051a19a628a53 Mon Sep 17 00:00:00 2001 From: Marc Khouzam Date: Wed, 20 Apr 2011 18:10:37 +0000 Subject: [PATCH] Bug 340697: Auto attach to processes forked by the process being debugged --- .../ui/launching/GdbDebuggerPage.java | 13 ++- .../ui/launching/LaunchUIMessages.properties | 1 + .../gdb/IGDBLaunchConfigurationConstants.java | 16 +++ .../launching/FinalLaunchSequence_7_2.java | 101 ++++++++++++++++++ .../gdb/service/command/GDBControl_7_2.java | 13 ++- .../mi/service/command/CommandFactory.java | 6 ++ .../MIRunControlEventProcessor_7_0.java | 31 ++++-- .../commands/MIGDBSetDetachOnFork.java | 29 +++++ 8 files changed, 199 insertions(+), 11 deletions(-) create mode 100644 dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/FinalLaunchSequence_7_2.java create mode 100644 dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIGDBSetDetachOnFork.java 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 e3c8322279b..4f3f0efeb09 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 @@ -52,6 +52,7 @@ public class GdbDebuggerPage extends AbstractCDebuggerPage implements Observer { protected Button fNonStopCheckBox; protected Button fReverseCheckBox; protected Button fUpdateThreadlistOnSuspend; + protected Button fDebugOnFork; private IMILaunchConfigurationComponent fSolibBlock; private boolean fIsInitializing = false; @@ -79,6 +80,8 @@ public class GdbDebuggerPage extends AbstractCDebuggerPage implements Observer { IGDBLaunchConfigurationConstants.DEBUGGER_REVERSE_DEFAULT); configuration.setAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_UPDATE_THREADLIST_ON_SUSPEND, IGDBLaunchConfigurationConstants.DEBUGGER_UPDATE_THREADLIST_ON_SUSPEND_DEFAULT); + configuration.setAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_DEBUG_ON_FORK, + IGDBLaunchConfigurationConstants.DEBUGGER_DEBUG_ON_FORK_DEFAULT); if (fSolibBlock != null) fSolibBlock.setDefaults(configuration); @@ -130,6 +133,8 @@ public class GdbDebuggerPage extends AbstractCDebuggerPage implements Observer { IGDBLaunchConfigurationConstants.DEBUGGER_REVERSE_DEFAULT); boolean updateThreadsOnSuspend = getBooleanAttr(configuration, IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_UPDATE_THREADLIST_ON_SUSPEND, IGDBLaunchConfigurationConstants.DEBUGGER_UPDATE_THREADLIST_ON_SUSPEND_DEFAULT); + boolean debugOnFork = getBooleanAttr(configuration, IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_DEBUG_ON_FORK, + IGDBLaunchConfigurationConstants.DEBUGGER_DEBUG_ON_FORK_DEFAULT); if (fSolibBlock != null) fSolibBlock.initializeFrom(configuration); @@ -138,6 +143,7 @@ public class GdbDebuggerPage extends AbstractCDebuggerPage implements Observer { fNonStopCheckBox.setSelection(nonStopMode); fReverseCheckBox.setSelection(reverseEnabled); fUpdateThreadlistOnSuspend.setSelection(updateThreadsOnSuspend); + fDebugOnFork.setSelection(debugOnFork); setInitializing(false); } @@ -152,8 +158,9 @@ public class GdbDebuggerPage extends AbstractCDebuggerPage implements Observer { configuration.setAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_REVERSE, fReverseCheckBox.getSelection()); configuration.setAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_UPDATE_THREADLIST_ON_SUSPEND, - fUpdateThreadlistOnSuspend.getSelection()); - + fUpdateThreadlistOnSuspend.getSelection()); + configuration.setAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_DEBUG_ON_FORK, + fDebugOnFork.getSelection()); if (fSolibBlock != null) fSolibBlock.performApply(configuration); } @@ -303,6 +310,8 @@ public class GdbDebuggerPage extends AbstractCDebuggerPage implements Observer { 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$ + + fDebugOnFork = addCheckbox(subComp, LaunchUIMessages.getString("GDBDebuggerPage.Automatically_debug_forked_processes")); //$NON-NLS-1$ } public void createSolibTab(TabFolder tabFolder) { 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 01ff2183232..5262c153f75 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 @@ -24,6 +24,7 @@ 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 +GDBDebuggerPage.Automatically_debug_forked_processes=Automatically debug forked processes (Note: Requires Multi Process 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 2bf84b89c6e..54233ba38dd 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 @@ -96,6 +96,15 @@ public class IGDBLaunchConfigurationConstants { */ public static final String ATTR_DEBUGGER_POST_MORTEM_TYPE = GdbPlugin.PLUGIN_ID + ".POST_MORTEM_TYPE"; //$NON-NLS-1$ + /** + * Launch configuration attribute key. Boolean value to set the 'detach-on-fork' GDB option. + * When detach-on-fork is off, we will automatically attach to forked processes. This will yield + * a multi-process session, which is supported with GDB >= 7.2 + * Note that detach-on-fork == !ATTR_DEBUGGER_DEBUG_ON_FORK + * @since 4.0 + */ + public static final String ATTR_DEBUGGER_DEBUG_ON_FORK = GdbPlugin.PLUGIN_ID + ".DEBUG_ON_FORK"; //$NON-NLS-1$ + /** * Launch configuration attribute value. The key is ATTR_DEBUG_NAME. @@ -159,5 +168,12 @@ public class IGDBLaunchConfigurationConstants { * @since 3.0 */ public static final String DEBUGGER_POST_MORTEM_TYPE_DEFAULT = DEBUGGER_POST_MORTEM_CORE_FILE; + + /** + * Launch configuration attribute value. The key is ATTR_DEBUGGER_DEBUG_ON_FORK. + * @since 4.0 + */ + public static final boolean DEBUGGER_DEBUG_ON_FORK_DEFAULT = false; + } diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/FinalLaunchSequence_7_2.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/FinalLaunchSequence_7_2.java new file mode 100644 index 00000000000..b1bf6cea197 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/FinalLaunchSequence_7_2.java @@ -0,0 +1,101 @@ +/******************************************************************************* + * Copyright (c) 2011 Ericsson 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: + * Ericsson - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.gdb.launching; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +import org.eclipse.cdt.debug.core.CDebugUtils; +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants; +import org.eclipse.cdt.dsf.concurrent.ImmediateExecutor; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.concurrent.RequestMonitorWithProgress; +import org.eclipse.cdt.dsf.gdb.IGDBLaunchConfigurationConstants; +import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin; +import org.eclipse.cdt.dsf.gdb.service.command.IGDBControl; +import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo; +import org.eclipse.cdt.dsf.service.DsfServicesTracker; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; + +/** + * Subclass for GDB >= 7.2. + * + * @since 4.0 + */ +public class FinalLaunchSequence_7_2 extends FinalLaunchSequence { + + private IGDBControl fGdbControl; + private DsfSession fSession; + + // The launchConfiguration attributes + private Map fAttributes; + + public FinalLaunchSequence_7_2(DsfSession session, Map attributes, RequestMonitorWithProgress rm) { + super(session, attributes, rm); + fSession = session; + fAttributes = attributes; + } + + @Override + protected String[] getExecutionOrder(String group) { + if (GROUP_TOP_LEVEL.equals(group)) { + // Initialize the list with the base class' steps + // We need to create a list that we can modify, which is why we create our own ArrayList. + List orderList = new ArrayList(Arrays.asList(super.getExecutionOrder(GROUP_TOP_LEVEL))); + + // Now insert our steps right after the initialization of the base class. + orderList.add(orderList.indexOf("stepInitializeFinalLaunchSequence") + 1, "stepInitializeFinalLaunchSequence_7_2"); //$NON-NLS-1$ //$NON-NLS-2$ + orderList.add(orderList.indexOf("stepSetBreakpointPending") + 1, "stepDetachOnFork"); //$NON-NLS-1$ //$NON-NLS-2$ + + return orderList.toArray(new String[orderList.size()]); + } + + return null; + } + + /** + * Initialize the members of the FinalLaunchSequence_7_2 class. + * This step is mandatory for the rest of the sequence to complete. + */ + @Execute + public void stepInitializeFinalLaunchSequence_7_2(RequestMonitor rm) { + DsfServicesTracker tracker = new DsfServicesTracker(GdbPlugin.getBundleContext(), fSession.getId()); + fGdbControl = tracker.getService(IGDBControl.class); + tracker.dispose(); + + if (fGdbControl == null) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.INTERNAL_ERROR, "Cannot obtain service", null)); //$NON-NLS-1$ + rm.done(); + return; + } + + rm.done(); + } + + /** + * Tell GDB whether to automatically attach to a forked process or not. + */ + @Execute + public void stepDetachOnFork(final RequestMonitor rm) { + boolean debugOnFork = CDebugUtils.getAttribute(fAttributes, + IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_DEBUG_ON_FORK, + IGDBLaunchConfigurationConstants.DEBUGGER_DEBUG_ON_FORK_DEFAULT); + + fGdbControl.queueCommand( + fGdbControl.getCommandFactory().createMIGDBSetDetachOnFork(fGdbControl.getContext(), !debugOnFork), + new DataRequestMonitor(ImmediateExecutor.getInstance(), rm)); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/command/GDBControl_7_2.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/command/GDBControl_7_2.java index 5c4dd1846ed..66e0220d288 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/command/GDBControl_7_2.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/command/GDBControl_7_2.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2010 Ericsson and others. + * Copyright (c) 2010, 2011 Ericsson 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 @@ -10,6 +10,11 @@ *******************************************************************************/ package org.eclipse.cdt.dsf.gdb.service.command; +import java.util.Map; + +import org.eclipse.cdt.dsf.concurrent.RequestMonitorWithProgress; +import org.eclipse.cdt.dsf.concurrent.Sequence; +import org.eclipse.cdt.dsf.gdb.launching.FinalLaunchSequence_7_2; import org.eclipse.cdt.dsf.mi.service.command.CommandFactory; import org.eclipse.cdt.dsf.service.DsfSession; import org.eclipse.debug.core.ILaunchConfiguration; @@ -23,4 +28,10 @@ public class GDBControl_7_2 extends GDBControl_7_0 implements IGDBControl { super(session, config, factory); setUseThreadGroupOptions(true); } + + @Override + protected Sequence getCompleteInitializationSequence(Map attributes, RequestMonitorWithProgress rm) { + return new FinalLaunchSequence_7_2(getSession(), attributes, rm); + } + } diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/CommandFactory.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/CommandFactory.java index 735f5aae65a..aaaaee1c71b 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/CommandFactory.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/CommandFactory.java @@ -96,6 +96,7 @@ import org.eclipse.cdt.dsf.mi.service.command.commands.MIGDBSet; import org.eclipse.cdt.dsf.mi.service.command.commands.MIGDBSetArgs; import org.eclipse.cdt.dsf.mi.service.command.commands.MIGDBSetAutoSolib; import org.eclipse.cdt.dsf.mi.service.command.commands.MIGDBSetBreakpointPending; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIGDBSetDetachOnFork; import org.eclipse.cdt.dsf.mi.service.command.commands.MIGDBSetEnv; import org.eclipse.cdt.dsf.mi.service.command.commands.MIGDBSetNonStop; import org.eclipse.cdt.dsf.mi.service.command.commands.MIGDBSetPagination; @@ -596,6 +597,11 @@ public class CommandFactory { return new MIGDBSetBreakpointPending(ctx, enable); } + /** @since 4.0 */ + public ICommand createMIGDBSetDetachOnFork(ICommandControlDMContext ctx, boolean detach) { + return new MIGDBSetDetachOnFork(ctx, detach); + } + public ICommand createMIGDBSetEnv(ICommandControlDMContext dmc, String name) { return new MIGDBSetEnv(dmc, name); } diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/MIRunControlEventProcessor_7_0.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/MIRunControlEventProcessor_7_0.java index 06690d5a1b2..5591aeb5fa9 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/MIRunControlEventProcessor_7_0.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/MIRunControlEventProcessor_7_0.java @@ -211,8 +211,7 @@ public class MIRunControlEventProcessor_7_0 fCommandControl.getSession().dispatchEvent(event, fCommandControl.getProperties()); } - } else if ("thread-group-created".equals(miEvent) || "thread-group-started".equals(miEvent) || //$NON-NLS-1$ //$NON-NLS-2$ - "thread-group-exited".equals(miEvent)) { //$NON-NLS-1$ + } else if ("thread-group-created".equals(miEvent) || "thread-group-started".equals(miEvent)) { //$NON-NLS-1$ //$NON-NLS-2$ String groupId = null; String pId = null; @@ -242,13 +241,29 @@ public class MIRunControlEventProcessor_7_0 if (pId != null && procService != null) { IProcessDMContext procDmc = procService.createProcessContext(fControlDmc, pId); - MIEvent event = null; - if ("thread-group-created".equals(miEvent) || "thread-group-started".equals(miEvent)) { //$NON-NLS-1$ //$NON-NLS-2$ - event = new MIThreadGroupCreatedEvent(procDmc, exec.getToken(), groupId); - } else if ("thread-group-exited".equals(miEvent)) { //$NON-NLS-1$ - event = new MIThreadGroupExitedEvent(procDmc, exec.getToken(), groupId); - } + MIEvent event = new MIThreadGroupCreatedEvent(procDmc, exec.getToken(), groupId); + fCommandControl.getSession().dispatchEvent(event, fCommandControl.getProperties()); + } + } else if ("thread-group-exited".equals(miEvent)) { //$NON-NLS-1$ + String groupId = null; + MIResult[] results = exec.getMIResults(); + for (int i = 0; i < results.length; i++) { + String var = results[i].getVariable(); + MIValue val = results[i].getMIValue(); + if (var.equals("id")) { //$NON-NLS-1$ + if (val instanceof MIConst) { + groupId = ((MIConst) val).getString().trim(); + } + } + } + + IMIProcesses procService = fServicesTracker.getService(IMIProcesses.class); + if (procService != null) { + IContainerDMContext containerDmc = procService.createContainerContextFromGroupId(fControlDmc, groupId); + IProcessDMContext procDmc = DMContexts.getAncestorOfType(containerDmc, IProcessDMContext.class); + + MIEvent event = new MIThreadGroupExitedEvent(procDmc, exec.getToken(), groupId); fCommandControl.getSession().dispatchEvent(event, fCommandControl.getProperties()); } } diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIGDBSetDetachOnFork.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIGDBSetDetachOnFork.java new file mode 100644 index 00000000000..c38bf8f22e3 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIGDBSetDetachOnFork.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * Copyright (c) 2011 Ericsson 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: + * Ericsson - Initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.mi.service.command.commands; + +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlDMContext; + +/** + * + * -gdb-set detach-on-fork [on|off] + * + * When 'on', tells GDB to detach from process that has been forked. + * When 'off', automatically starts debugging a forked process in a multi-process session. + * + * @since 4.0 + */ +public class MIGDBSetDetachOnFork extends MIGDBSet +{ + public MIGDBSetDetachOnFork(ICommandControlDMContext ctx, boolean detach) { + super(ctx, new String[] {"detach-on-fork", detach ? "on" : "off"});//$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$ + } +} \ No newline at end of file