Download
Getting Started
Members
Projects
Community
Marketplace
Events
Planet Eclipse
Newsletter
Videos
Participate
Report a Bug
Forums
Mailing Lists
Wiki
IRC
How to Contribute
Working Groups
Automotive
Internet of Things
LocationTech
Long-Term Support
PolarSys
Science
OpenMDM
More
Community
Marketplace
Events
Planet Eclipse
Newsletter
Videos
Participate
Report a Bug
Forums
Mailing Lists
Wiki
IRC
How to Contribute
Working Groups
Automotive
Internet of Things
LocationTech
Long-Term Support
PolarSys
Science
OpenMDM
Toggle navigation
Bugzilla – Attachment 192993 Details for
Bug 337893
[multi-process][breakpoints] Setting breakpoints on a running target does not work with multi-process
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
Log In
[x]
|
Terms of Use
|
Copyright Agent
Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read
this important communication.
[patch]
Fix to interrupt a thread for every process
z.bpInterruptNonStop3.patch (text/plain), 41.96 KB, created by
Marc Khouzam
on 2011-04-11 20:43:29 EDT
(
hide
)
Description:
Fix to interrupt a thread for every process
Filename:
MIME Type:
Creator:
Marc Khouzam
Created:
2011-04-11 20:43:29 EDT
Size:
41.96 KB
patch
obsolete
>### Eclipse Workspace Patch 1.0 >#P org.eclipse.cdt.dsf.gdb >Index: src/org/eclipse/cdt/dsf/gdb/service/GDBRunControl_7_0_NS.java >=================================================================== >RCS file: /cvsroot/tools/org.eclipse.cdt/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBRunControl_7_0_NS.java,v >retrieving revision 1.33 >diff -u -r1.33 GDBRunControl_7_0_NS.java >--- src/org/eclipse/cdt/dsf/gdb/service/GDBRunControl_7_0_NS.java 10 Apr 2011 01:28:19 -0000 1.33 >+++ src/org/eclipse/cdt/dsf/gdb/service/GDBRunControl_7_0_NS.java 12 Apr 2011 00:38:59 -0000 >@@ -13,11 +13,14 @@ > package org.eclipse.cdt.dsf.gdb.service; > > import java.util.HashMap; >+import java.util.HashSet; > import java.util.Hashtable; > import java.util.LinkedList; > import java.util.Map; >+import java.util.Set; > > import org.eclipse.cdt.core.IAddress; >+import org.eclipse.cdt.dsf.concurrent.CountingRequestMonitor; > import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; > import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants; > import org.eclipse.cdt.dsf.concurrent.ImmediateExecutor; >@@ -43,7 +46,6 @@ > import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMContext; > import org.eclipse.cdt.dsf.debug.service.command.ICommand; > import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService; >-import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlDMContext; > import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlShutdownDMEvent; > import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin; > import org.eclipse.cdt.dsf.gdb.internal.service.command.events.MITracepointSelectedEvent; >@@ -315,6 +317,7 @@ > > private ICommandControlService fConnection; > private CommandFactory fCommandFactory; >+ private IProcesses fProcessService; > > private boolean fTerminated = false; > >@@ -334,17 +337,17 @@ > } > > /** >- * Indicates that the next MIRunning event for this thread should be silenced. >+ * Set of threads for which the next MIRunning event should be silenced. > */ >- private IMIExecutionDMContext fDisableNextRunningEventDmc; >+ private Set<IMIExecutionDMContext> fDisableNextRunningEventDmcSet = new HashSet<IMIExecutionDMContext>(); > /** >- * Indicates that the next MISignal (MIStopped) event for this thread should be silenced. >+ * Set of threads for which the next MISignal (MIStopped) event should be silenced. > */ >- private IMIExecutionDMContext fDisableNextSignalEventDmc; >+ private Set<IMIExecutionDMContext> fDisableNextSignalEventDmcSet = new HashSet<IMIExecutionDMContext>(); > /** >- * Stores the silenced MIStopped event in case we need to use it for a failure. >+ * Map that stores the silenced MIStopped event for the specified thread, in case we need to use it for a failure. > */ >- private MIStoppedEvent fSilencedSignalEvent; >+ private Map<IMIExecutionDMContext,MIStoppedEvent> fSilencedSignalEventMap = new HashMap<IMIExecutionDMContext, MIStoppedEvent>(); > > /** > * This variable allows us to know if run control operation >@@ -379,6 +382,8 @@ > new Hashtable<String,String>()); > fConnection = getServicesTracker().getService(ICommandControlService.class); > fCommandFactory = getServicesTracker().getService(IMICommandControl.class).getCommandFactory(); >+ fProcessService = getServicesTracker().getService(IProcesses.class); >+ > getSession().addServiceEventListener(this, null); > rm.done(); > } >@@ -943,6 +948,22 @@ > * and finally resume that thread.. > * See https://bugs.eclipse.org/bugs/show_bug.cgi?id=242943 > * and https://bugs.eclipse.org/bugs/show_bug.cgi?id=282273 >+ * >+ * Note that for multi-process, we need to interrupt all processes >+ * that share the same binary before doing a breakpoint operation on any of >+ * those processes. For simplicity, the logic below interrupts one thread of >+ * every process being debugged, without differentiating on the executable. >+ * Although it may seem wasteful to interrupt all processes when not necessary, >+ * in truth it is not so much; when making a breakpoint operation in Eclipse, that >+ * operation is propagated to all processes anyway, so they will all need to be >+ * interrupted. The case where we are wasteful is when we start or stop debugging >+ * a process (starting a process, attaching to one, auto-attaching to one, >+ * detaching from one, terminating one); in those cases, we only want to apply the >+ * breakpoint operation to that one process and any other using the same binary. >+ * The wastefulness is not such a big deal for that case, and is worth the simpler >+ * solution. >+ * Of course, it can always be improved later on. >+ * See http://bugs.eclipse.org/337893 > * ******************************************************************************/ > > /** >@@ -962,12 +983,8 @@ > } > }; > >- // Keep track of if the target was available or not when we started the operation >- private boolean fTargetAvailable; >- // The container that needs to be suspended to perform the steps of the operation >- private IContainerDMContext fContainerDmcToSuspend; >- // The thread that we will actually suspend to make the container suspended. >- private IMIExecutionDMContext fExecutionDmcToSuspend; >+ // The set of threads that we will actually be suspended to make the containers suspended. >+ private Set<IMIExecutionDMContext> fExecutionDmcToSuspendSet = new HashSet<IMIExecutionDMContext>(); > > // Do we currently have an executeWithTargetAvailable() operation ongoing? > private boolean fOngoingOperation; >@@ -981,7 +998,7 @@ > // allow the global sequence to continue. > // Note that we couldn't use a CountingRequestMonitor because that type of RM > // needs to know in advance how many subRms it will track; the MultiRM allows us >- // to receive more steps to execute continuously, and be able to upate the MultiRM. >+ // to receive more steps to execute continuously, and be able to update the MultiRM. > private MultiRequestMonitor<RequestMonitor> fExecuteQueuedOpsStepMonitor; > // The number of batches of steps that are still being executing for potentially > // concurrent executeWithTargetAvailable() operations. >@@ -991,47 +1008,6 @@ > // Queue of executeWithTargetAvailable() operations that need to be processed. > private LinkedList<TargetAvailableOperationInfo> fOperationsPending = new LinkedList<TargetAvailableOperationInfo>(); > >- /** >- * Returns whether the target is available to perform operations >- * @since 4.0 >- */ >- protected boolean isTargetAvailable() { >- return fTargetAvailable; >- } >- >- /** @since 4.0 */ >- protected void setTargetAvailable(boolean available) { >- fTargetAvailable = available; >- } >- >- /** >- * Returns the container context that needs to be suspended to perform the >- * required operation. >- * @since 4.0 >- */ >- protected IContainerDMContext getContainerToSuspend() { >- return fContainerDmcToSuspend; >- } >- >- /** @since 4.0 */ >- protected void setContainerToSuspend(IContainerDMContext context) { >- fContainerDmcToSuspend = context; >- } >- >- /** >- * Returns the container context that needs to be suspended to perform the >- * required operation. >- * @since 4.0 >- */ >- protected IMIExecutionDMContext getExecutionToSuspend() { >- return fExecutionDmcToSuspend; >- } >- >- /** @since 4.0 */ >- protected void setExecutionToSuspend(IMIExecutionDMContext context) { >- fExecutionDmcToSuspend = context; >- } >- > /** > * Returns whether there is currently an ExecuteWithTargetAvailable() operation ongoing. > * @since 4.0 >@@ -1191,8 +1167,7 @@ > > > /** >- * This part of the sequence verifies if the execution context of interest >- * is suspended or not. >+ * This part of the sequence looks for all threads that will need to be suspended. > * @since 3.0 > */ > protected class IsTargetAvailableStep extends Sequence.Step { >@@ -1202,86 +1177,91 @@ > fCtx = ctx; > } > >+ private void getThreadToSuspend(IContainerDMContext containerDmc, final RequestMonitor rm) { >+ // If the process is running, get its first thread which we will need to suspend >+ fProcessService.getProcessesBeingDebugged( >+ containerDmc, >+ new DataRequestMonitor<IDMContext[]>(ImmediateExecutor.getInstance(), rm) { >+ @Override >+ protected void handleSuccess() { >+ IDMContext[] threads = getData(); >+ if (threads != null && threads.length > 0) { >+ // Choose the first thread as the one to suspend >+ fExecutionDmcToSuspendSet.add((IMIExecutionDMContext)threads[0]); >+ } >+ rm.done(); >+ } >+ }); >+ } >+ > @Override > public void execute(final RequestMonitor rm) { >- // In non-stop, for single address-space multi-process, >- // we need any one of the processes to be suspended. >- // Note that even if we can obtain a container dmc from fCtx >- // we will have issues because that context is probably not >- // fully formed (missing groupId and procId). So, we just >- // make it easy on ourselves and fetch the containers directly >- ICommandControlDMContext controlDmc = DMContexts.getAncestorOfType(fCtx, ICommandControlDMContext.class); >- IProcesses processControl = getServicesTracker().getService(IProcesses.class); >- processControl.getProcessesBeingDebugged( >- controlDmc, >- new DataRequestMonitor<IDMContext[]>(getExecutor(), rm) { >+ // Clear any old data before we start >+ fExecutionDmcToSuspendSet.clear(); >+ >+ // Get all processes being debugged to see which one are running >+ // and need to be interrupted >+ fProcessService.getProcessesBeingDebugged( >+ fConnection.getContext(), >+ new DataRequestMonitor<IDMContext[]>(ImmediateExecutor.getInstance(), rm) { > @Override > protected void handleSuccess() { > assert getData() != null; >- >+ > if (getData().length == 0) { > // Happens at startup, starting with GDB 7.0. >- // This means the target is available >- fTargetAvailable = true; >+ // This means the target is available. Nothing to do. >+ rm.done(); > } else { >- fTargetAvailable = false; >- // Choose the first process as the one to suspend if needed >- fContainerDmcToSuspend = (IContainerDMContext)(getData()[0]); >- for (IDMContext containerDmc : getData()) { >- if (isSuspended((IContainerDMContext)containerDmc)) { >- fTargetAvailable = true; >- break; >+ // Go through every process to see if it is running. >+ // If it is running, get its first thread so we can interrupt it. >+ CountingRequestMonitor crm = new CountingRequestMonitor(ImmediateExecutor.getInstance(), rm); >+ >+ int numThreadsToSuspend = 0; >+ for (IDMContext dmc : getData()) { >+ IContainerDMContext containerDmc = (IContainerDMContext)dmc; >+ if (!isSuspended(containerDmc)) { >+ numThreadsToSuspend++; >+ getThreadToSuspend(containerDmc, crm); > } > } >+ crm.setDoneCount(numThreadsToSuspend); > } >- rm.done(); > } > }); > } > }; > > /** >- * If the execution context of interest is not suspended, this step >- * will interrupt it. >+ * Suspended all the threads we have selected. > * @since 3.0 > */ > protected class MakeTargetAvailableStep extends Sequence.Step { > @Override > public void execute(final RequestMonitor rm) { >- if (!isTargetAvailable()) { >- // Instead of suspending the entire process, let's find its first thread and use that >- IProcesses processControl = getServicesTracker().getService(IProcesses.class); >- processControl.getProcessesBeingDebugged( >- fContainerDmcToSuspend, >- new DataRequestMonitor<IDMContext[]>(getExecutor(), rm) { >- @Override >- protected void handleSuccess() { >- assert getData() != null; >- assert getData().length > 0; >- >- fExecutionDmcToSuspend = (IMIExecutionDMContext)getData()[0]; >- >- assert fDisableNextRunningEventDmc == null; >- assert fDisableNextSignalEventDmc == null; >- >- // Don't broadcast the next stopped signal event >- fDisableNextSignalEventDmc = fExecutionDmcToSuspend; >- >- suspend(fExecutionDmcToSuspend, >- new RequestMonitor(getExecutor(), rm) { >- @Override >- protected void handleFailure() { >- // We weren't able to suspend, so abort the operation >- fDisableNextSignalEventDmc = null; >- super.handleFailure(); >- }; >- }); >- } >- }); >- } else { >- rm.done(); >+ // Interrupt every first thread of the running processes >+ CountingRequestMonitor crm = new CountingRequestMonitor(ImmediateExecutor.getInstance(), rm); >+ crm.setDoneCount(fExecutionDmcToSuspendSet.size()); >+ >+ for (final IMIExecutionDMContext thread : fExecutionDmcToSuspendSet) { >+ assert !fDisableNextRunningEventDmcSet.contains(thread); >+ assert !fDisableNextSignalEventDmcSet.contains(thread); >+ >+ // Don't broadcast the next stopped signal event >+ fDisableNextSignalEventDmcSet.add(thread); >+ >+ suspend(thread, >+ new RequestMonitor(ImmediateExecutor.getInstance(), crm) { >+ @Override >+ protected void handleFailure() { >+ // We weren't able to suspend, so abort the operation >+ fDisableNextSignalEventDmcSet.remove(thread); >+ super.handleFailure(); >+ }; >+ }); > } > } >+ > @Override > public void rollBack(RequestMonitor rm) { > Sequence.Step restoreStep = new RestoreTargetStateStep(); >@@ -1333,46 +1313,47 @@ > protected class RestoreTargetStateStep extends Sequence.Step { > @Override > public void execute(final RequestMonitor rm) { >- if (!isTargetAvailable()) { >- assert fDisableNextRunningEventDmc == null; >- fDisableNextRunningEventDmc = fExecutionDmcToSuspend; >- >+ // Resume every thread we had interrupted >+ CountingRequestMonitor crm = new CountingRequestMonitor(ImmediateExecutor.getInstance(), rm); >+ crm.setDoneCount(fExecutionDmcToSuspendSet.size()); >+ >+ for (final IMIExecutionDMContext thread : fExecutionDmcToSuspendSet) { >+ >+ assert !fDisableNextRunningEventDmcSet.contains(thread); >+ fDisableNextRunningEventDmcSet.add(thread); >+ > // Can't use the resume() call because we 'silently' stopped > // so resume() will not know we are actually stopped > fConnection.queueCommand( >- fCommandFactory.createMIExecContinue(fExecutionDmcToSuspend), >- new DataRequestMonitor<MIInfo>(getExecutor(), rm) { >+ fCommandFactory.createMIExecContinue(thread), >+ new DataRequestMonitor<MIInfo>(ImmediateExecutor.getInstance(), crm) { > @Override > protected void handleSuccess() { >- fSilencedSignalEvent = null; >+ fSilencedSignalEventMap.remove(thread); > super.handleSuccess(); > } > > @Override > protected void handleFailure() { > // Darn, we're unable to restart the target. Must cleanup! >- fDisableNextRunningEventDmc = null; >- >+ fDisableNextRunningEventDmcSet.remove(thread); >+ > // We must also sent the Stopped event that we had kept silent >- if (fSilencedSignalEvent != null) { >- eventDispatched(fSilencedSignalEvent); >- fSilencedSignalEvent = null; >+ MIStoppedEvent event = fSilencedSignalEventMap.remove(thread); >+ if (event != null) { >+ eventDispatched(event); > } else { > // Maybe the stopped event didn't arrive yet. > // We don't want to silence it anymore >- fDisableNextSignalEventDmc = null; >+ fDisableNextSignalEventDmcSet.remove(thread); > } > > super.handleFailure(); > } > }); >- >- } else { >- // We didn't suspend the thread, so we don't need to resume it >- rm.done(); > } > } >- }; >+ }; > > /* ****************************************************************************** > * End of section to support operations even when the target is unavailable. >@@ -1388,14 +1369,11 @@ > */ > @DsfServiceEventHandler > public void eventDispatched(final MIRunningEvent e) { >- if (fDisableNextRunningEventDmc != null && >- fDisableNextRunningEventDmc.equals(e.getDMContext())) { >- >- fDisableNextRunningEventDmc = null; >- >+ if (fDisableNextRunningEventDmcSet.remove(e.getDMContext())) { > // Don't broadcast the running event > return; > } >+ > getSession().dispatchEvent(new ResumedEvent(e.getDMContext(), e), getProperties()); > } > >@@ -1457,11 +1435,10 @@ > } > } > } >- if (fDisableNextSignalEventDmc != null && e instanceof MISignalEvent && >- fDisableNextSignalEventDmc.equals(e.getDMContext())) { >- >- fDisableNextSignalEventDmc = null; >- fSilencedSignalEvent = e; >+ >+ IMIExecutionDMContext threadDmc = DMContexts.getAncestorOfType(e.getDMContext(), IMIExecutionDMContext.class); >+ if (e instanceof MISignalEvent && fDisableNextSignalEventDmcSet.remove(threadDmc)) { >+ fSilencedSignalEventMap.put(threadDmc, e); > > // Don't broadcast the stopped event > return; >Index: src/org/eclipse/cdt/dsf/gdb/service/GDBRunControl_7_2_NS.java >=================================================================== >RCS file: /cvsroot/tools/org.eclipse.cdt/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBRunControl_7_2_NS.java,v >retrieving revision 1.3 >diff -u -r1.3 GDBRunControl_7_2_NS.java >--- src/org/eclipse/cdt/dsf/gdb/service/GDBRunControl_7_2_NS.java 6 Apr 2011 03:01:34 -0000 1.3 >+++ src/org/eclipse/cdt/dsf/gdb/service/GDBRunControl_7_2_NS.java 12 Apr 2011 00:38:59 -0000 >@@ -1,62 +1,39 @@ > /******************************************************************************* >- * Copyright (c) 2011 Wind River Systems and others. >+ * 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: >- * Wind River Systems - initial API and implementation >- * Ericsson - Support for multi-process for Linux, GDB 7.2 >+ * Ericsson - initial API and implementation > *******************************************************************************/ > > package org.eclipse.cdt.dsf.gdb.service; > >-import java.util.HashMap; >-import java.util.HashSet; > import java.util.Hashtable; >-import java.util.LinkedList; >-import java.util.Map; >-import java.util.Set; > > import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; > import org.eclipse.cdt.dsf.concurrent.ImmediateExecutor; >-import org.eclipse.cdt.dsf.concurrent.MultiRequestMonitor; > import org.eclipse.cdt.dsf.concurrent.RequestMonitor; >-import org.eclipse.cdt.dsf.concurrent.Sequence; >-import org.eclipse.cdt.dsf.concurrent.Sequence.Step; > import org.eclipse.cdt.dsf.datamodel.DMContexts; >-import org.eclipse.cdt.dsf.datamodel.IDMContext; >-import org.eclipse.cdt.dsf.datamodel.IDMEvent; >-import org.eclipse.cdt.dsf.debug.service.IBreakpoints.IBreakpointsTargetDMContext; >-import org.eclipse.cdt.dsf.debug.service.IProcesses; > import org.eclipse.cdt.dsf.debug.service.IRunControl; > import org.eclipse.cdt.dsf.debug.service.IRunControl2; > 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.mi.service.IMICommandControl; > import org.eclipse.cdt.dsf.mi.service.IMIContainerDMContext; > import org.eclipse.cdt.dsf.mi.service.IMIExecutionDMContext; > import org.eclipse.cdt.dsf.mi.service.IMIRunControl; >-import org.eclipse.cdt.dsf.mi.service.MIBreakpoints.MIBreakpointDMContext; > import org.eclipse.cdt.dsf.mi.service.command.CommandFactory; >-import org.eclipse.cdt.dsf.mi.service.command.events.MIBreakpointHitEvent; >-import org.eclipse.cdt.dsf.mi.service.command.events.MIRunningEvent; >-import org.eclipse.cdt.dsf.mi.service.command.events.MISignalEvent; >-import org.eclipse.cdt.dsf.mi.service.command.events.MIStoppedEvent; > import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo; >-import org.eclipse.cdt.dsf.service.DsfServiceEventHandler; > import org.eclipse.cdt.dsf.service.DsfSession; > import org.eclipse.core.runtime.IStatus; > import org.eclipse.core.runtime.Status; > > /** >- * Version of the non-stop runControl for GDB 7.2 >- * This class handles multi-process for Linux which requires >- * us to interrupt different processes at the same time to be >- * able to set breakpoints. >- * This class was created for bug 337893 >+ * Version of the non-stop runControl for GDB 7.2. >+ * > * @since 4.0 > */ > public class GDBRunControl_7_2_NS extends GDBRunControl_7_0_NS >@@ -65,21 +42,6 @@ > private ICommandControlService fConnection; > private CommandFactory fCommandFactory; > >- /** >- * Set of threads for which the next MIRunning event should be silenced. >- */ >- private Set<IMIExecutionDMContext> fDisableNextRunningEventDmc = new HashSet<IMIExecutionDMContext>(); >- /** >- * Set of threads for which the next MISignal (MIStopped) event should be silenced. >- */ >- private Set<IMIExecutionDMContext> fDisableNextSignalEventDmc = new HashSet<IMIExecutionDMContext>(); >- /** >- * Map that stores the silenced MIStopped event for the specified thread, in case we need to use it for a failure. >- */ >- private Map<IMIExecutionDMContext,MIStoppedEvent> fSilencedSignalEvent = new HashMap<IMIExecutionDMContext, MIStoppedEvent>(); >- >- private Map<IDMContext, ExecuteWithTargetAvailableOperation> execWithTargetAvailMap = new HashMap<IDMContext, ExecuteWithTargetAvailableOperation>(); >- > /////////////////////////////////////////////////////////////////////////// > // Initialization and shutdown > /////////////////////////////////////////////////////////////////////////// >@@ -119,6 +81,9 @@ > super.shutdown(rm); > } > >+ // Now that the flag --thread-group is globally supported >+ // by GDB 7.2, we have to make sure not to use it twice. >+ // Bug 340262 > @Override > public void suspend(final IExecutionDMContext context, final RequestMonitor rm) { > assert context != null; >@@ -145,6 +110,9 @@ > }); > } > >+ // Now that the flag --thread-group is globally supported >+ // by GDB 7.2, we have to make sure not to use it twice. >+ // Bug 340262 > @Override > public void resume(final IExecutionDMContext context, final RequestMonitor rm) { > assert context != null; >@@ -201,460 +169,4 @@ > private void doResumeContainer(IMIContainerDMContext context, final RequestMonitor rm) { > fConnection.queueCommand(fCommandFactory.createMIExecContinue(context), new DataRequestMonitor<MIInfo>(getExecutor(), rm)); > } >- >- @Override >- public void executeWithTargetAvailable(IDMContext ctx, final Sequence.Step[] steps, final RequestMonitor rm) { >- ExecuteWithTargetAvailableOperation operation = execWithTargetAvailMap.get(ctx); >- if (operation == null) { >- operation = new ExecuteWithTargetAvailableOperation(ctx); >- execWithTargetAvailMap.put(ctx, operation); >- } >- operation.executeWithTargetAvailable(steps, rm); >- } >- >- /* ****************************************************************************** >- * Section to support making operations even when the target is unavailable. >- * >- * Although one would expect to be able to make commands all the time when >- * in non-stop mode, it turns out that GDB has trouble with some commands >- * like breakpoints. The safe way to do it is to make sure we have at least >- * one thread suspended. >- * >- * Basically, we must make sure one thread is suspended before making >- * certain operations (currently breakpoints). If that is not the case, we must >- * first suspend one thread, then perform the specified operations, >- * and finally resume that thread.. >- * See https://bugs.eclipse.org/bugs/show_bug.cgi?id=242943 >- * and https://bugs.eclipse.org/bugs/show_bug.cgi?id=282273 >- * >- * Note that for multi-process on Linux, the correct container must be suspended for >- * the breakpoint to be inserted on that container. This means that we need to be >- * able to interrupt multiple processes at the same time to insert a breakpoint >- * in each one of them. >- * See http://bugs.eclipse.org/337893 >- * ******************************************************************************/ >- >- protected class ExecuteWithTargetAvailableOperation { >- /** >- * Utility class to store the parameters of the executeWithTargetAvailable() operations. >- */ >- public class TargetAvailableOperationInfo { >- public Sequence.Step[] steps; >- public RequestMonitor rm; >- >- public TargetAvailableOperationInfo(Step[] steps, RequestMonitor rm) { >- super(); >- this.steps = steps; >- this.rm = rm; >- } >- }; >- >- public IDMContext fCtx; >- // Keep track of if the target was available or not when we started the operation >- public boolean fTargetAvailable; >- // The container that needs to be suspended to perform the steps of the operation >- public IContainerDMContext fContainerDmcToSuspend; >- // The thread that we will actually suspend to make the container suspended. >- public IMIExecutionDMContext fExecutionDmcToSuspend; >- >- // Do we currently have an executeWithTargetAvailable() operation ongoing? >- public boolean fOngoingOperation; >- // Are we currently executing steps passed into executeWithTargetAvailable()? >- // This allows us to know if we can add more steps to execute or if we missed >- // our opportunity >- public boolean fCurrentlyExecutingSteps; >- >- // MultiRequestMonitor that allows us to track all the different steps we are >- // executing. Once all steps are executed, we can complete this MultiRM and >- // allow the global sequence to continue. >- // Note that we couldn't use a CountingRequestMonitor because that type of RM >- // needs to know in advance how many subRms it will track; the MultiRM allows us >- // to receive more steps to execute continuously, and be able to upate the MultiRM. >- public MultiRequestMonitor<RequestMonitor> fExecuteQueuedOpsStepMonitor; >- // The number of batches of steps that are still being executing for potentially >- // concurrent executeWithTargetAvailable() operations. >- // Once this gets to zero, we know we have executed all the steps we were aware of >- // and we can complete the operation. >- public int fNumStepsStillExecuting; >- // Queue of executeWithTargetAvailable() operations that need to be processed. >- public LinkedList<TargetAvailableOperationInfo> fOperationsPending = new LinkedList<TargetAvailableOperationInfo>(); >- >- public ExecuteWithTargetAvailableOperation(IDMContext ctx) { >- fCtx = ctx; >- } >- >- /** >- * This method takes care of executing a batch of steps that were passed to >- * ExecuteWithTargetAvailable(). The method is used to track the progress >- * of all these batches of steps, so that we know exactly when all of them >- * have been completed and the global sequence can be completed. >- */ >- protected void executeSteps(final TargetAvailableOperationInfo info) { >- fNumStepsStillExecuting++; >- >- // This RM propagates any error to the original rm of the actual steps. >- // Even in case of errors for these steps, we want to continue the overall sequence >- RequestMonitor stepsRm = new RequestMonitor(ImmediateExecutor.getInstance(), null) { >- @Override >- protected void handleCompleted() { >- info.rm.setStatus(getStatus()); >- // It is important to call rm.done() right away. >- // This is because some other operation we are performing might be waiting >- // for this one to be done. If we try to wait for the entire sequence to be >- // done, then we will never finish because one monitor will never show as >- // done, waiting for the second one. >- info.rm.done(); >- >- fExecuteQueuedOpsStepMonitor.requestMonitorDone(this); >- fNumStepsStillExecuting--; >- if (fNumStepsStillExecuting == 0) { >- fExecuteQueuedOpsStepMonitor.doneAdding(); >- } >- } >- }; >- >- fExecuteQueuedOpsStepMonitor.add(stepsRm); >- >- getExecutor().execute(new Sequence(getExecutor(), stepsRm) { >- @Override public Step[] getSteps() { return info.steps; } >- }); >- } >- >- public void executeWithTargetAvailable(final Sequence.Step[] steps, final RequestMonitor rm) { >- if (!fOngoingOperation) { >- // We are the first operation of this kind currently requested >- // so we need to start the sequence >- fOngoingOperation = true; >- >- // We always go through our queue, even if we only have a single call to this method >- fOperationsPending.add(new TargetAvailableOperationInfo(steps, rm)); >- >- // Steps that need to be executed to perform the operation >- final Step[] sequenceSteps = new Step[] { >- new IsTargetAvailableStep(), >- new MakeTargetAvailableStep(), >- new ExecuteQueuedOperationsStep(), >- new RestoreTargetStateStep(), >- }; >- >- // Once all the sequence is completed, we need to see if we have received >- // another request that we now need to process >- RequestMonitor sequenceCompletedRm = new RequestMonitor(getExecutor(), null) { >- @Override >- protected void handleCompleted() { >- fOngoingOperation = false; >- >- if (fOperationsPending.size() > 0) { >- // Darn, more operations came in. Trigger their processing >- // by calling executeWithTargetAvailable() on the last one >- TargetAvailableOperationInfo info = fOperationsPending.removeLast(); >- executeWithTargetAvailable(info.steps, info.rm); >- } else { >- execWithTargetAvailMap.remove(fCtx); >- } >- // no other rm.done() needs to be called, they have all been handled already >- } >- }; >- >- getExecutor().execute(new Sequence(getExecutor(), sequenceCompletedRm) { >- @Override public Step[] getSteps() { return sequenceSteps; } >- }); >- } else { >- // We are currently already executing such an operation >- // If we are still in the process of executing steps, let's include this new set of steps. >- // This is important because some steps may depend on these new ones. >- if (fCurrentlyExecutingSteps) { >- executeSteps(new TargetAvailableOperationInfo(steps, rm)); >- } else { >- // Too late to execute the new steps, so queue them for later >- fOperationsPending.add(new TargetAvailableOperationInfo(steps, rm)); >- } >- } >- } >- >- >- /** >- * This part of the sequence verifies if the execution context of interest >- * is suspended or not. >- */ >- public class IsTargetAvailableStep extends Sequence.Step { >- @Override >- public void execute(final RequestMonitor rm) { >- fContainerDmcToSuspend = DMContexts.getAncestorOfType(fCtx, IMIContainerDMContext.class); >- if (fContainerDmcToSuspend != null) { >- fTargetAvailable = isSuspended(fContainerDmcToSuspend); >- rm.done(); >- return; >- } >- >- // If we get here, we have to get the list of processes to know if any of >- // them is suspended. >- ICommandControlDMContext controlDmc = DMContexts.getAncestorOfType(fCtx, ICommandControlDMContext.class); >- IProcesses processControl = getServicesTracker().getService(IProcesses.class); >- processControl.getProcessesBeingDebugged( >- controlDmc, >- new DataRequestMonitor<IDMContext[]>(getExecutor(), rm) { >- @Override >- protected void handleSuccess() { >- assert getData() != null; >- >- if (getData().length == 0) { >- // Happens at startup, starting with GDB 7.0. >- // This means the target is available >- fTargetAvailable = true; >- } else { >- fTargetAvailable = false; >- // Choose the first process as the one to suspend if needed >- fContainerDmcToSuspend = (IContainerDMContext)(getData()[0]); >- for (IDMContext containerDmc : getData()) { >- if (isSuspended((IContainerDMContext)containerDmc)) { >- fTargetAvailable = true; >- break; >- } >- } >- } >- rm.done(); >- } >- }); >- } >- }; >- >- /** >- * If the execution context of interest is not suspended, this step >- * will interrupt it. >- */ >- public class MakeTargetAvailableStep extends Sequence.Step { >- @Override >- public void execute(final RequestMonitor rm) { >- if (!fTargetAvailable) { >- // Instead of suspending the entire process, let's find its first thread and use that >- IProcesses processControl = getServicesTracker().getService(IProcesses.class); >- processControl.getProcessesBeingDebugged( >- fContainerDmcToSuspend, >- new DataRequestMonitor<IDMContext[]>(getExecutor(), rm) { >- @Override >- protected void handleSuccess() { >- assert getData() != null; >- assert getData().length > 0; >- >- fExecutionDmcToSuspend = (IMIExecutionDMContext)getData()[0]; >- >- assert !fDisableNextRunningEventDmc.contains(fExecutionDmcToSuspend); >- assert !fDisableNextSignalEventDmc.contains(fExecutionDmcToSuspend); >- >- // Don't broadcast the next stopped signal event >- fDisableNextSignalEventDmc.add(fExecutionDmcToSuspend); >- >- suspend(fExecutionDmcToSuspend, >- new RequestMonitor(getExecutor(), rm) { >- @Override >- protected void handleFailure() { >- // We weren't able to suspend, so abort the operation >- fDisableNextSignalEventDmc.remove(fExecutionDmcToSuspend); >- super.handleFailure(); >- }; >- }); >- } >- }); >- } else { >- rm.done(); >- } >- } >- @Override >- public void rollBack(RequestMonitor rm) { >- Sequence.Step restoreStep = new RestoreTargetStateStep(); >- restoreStep.execute(rm); >- } >- }; >- >- /** >- * This step of the sequence takes care of executing all the steps that >- * were passed to ExecuteWithTargetAvailable(). >- */ >- public class ExecuteQueuedOperationsStep extends Sequence.Step { >- @Override >- public void execute(final RequestMonitor rm) { >- fCurrentlyExecutingSteps = true; >- >- // It is important to use an ImmediateExecutor for this RM, to make sure we don't risk getting a new >- // call to ExecuteWithTargetAvailable() when we just finished executing the steps. >- fExecuteQueuedOpsStepMonitor = new MultiRequestMonitor<RequestMonitor>(ImmediateExecutor.getInstance(), rm) { >- @Override >- protected void handleCompleted() { >- assert fOperationsPending.size() == 0; >- >- // We don't handle errors here. Instead, we have already propagated any >- // errors to each rm for each set of steps >- >- fCurrentlyExecutingSteps = false; >- // Continue the sequence >- rm.done(); >- } >- }; >- // Tell the RM that we need to confirm when we are done adding sub-rms >- fExecuteQueuedOpsStepMonitor.requireDoneAdding(); >- >- // All pending operations are independent of each other so we can >- // run them concurrently. >- while (fOperationsPending.size() > 0) { >- executeSteps(fOperationsPending.poll()); >- } >- } >- }; >- >- /** >- * If the sequence had to interrupt the execution context of interest, >- * this step will resume it again to reach the same state as when we started. >- */ >- public class RestoreTargetStateStep extends Sequence.Step { >- @Override >- public void execute(final RequestMonitor rm) { >- if (!fTargetAvailable) { >- assert !fDisableNextRunningEventDmc.contains(fExecutionDmcToSuspend); >- fDisableNextRunningEventDmc.add(fExecutionDmcToSuspend); >- >- // Can't use the resume() call because we 'silently' stopped >- // so resume() will not know we are actually stopped >- fConnection.queueCommand( >- fCommandFactory.createMIExecContinue(fExecutionDmcToSuspend), >- new DataRequestMonitor<MIInfo>(getExecutor(), rm) { >- @Override >- protected void handleSuccess() { >- fSilencedSignalEvent.remove(fExecutionDmcToSuspend); >- super.handleSuccess(); >- } >- >- @Override >- protected void handleFailure() { >- // Darn, we're unable to restart the target. Must cleanup! >- fDisableNextRunningEventDmc.remove(fExecutionDmcToSuspend); >- >- // We must also sent the Stopped event that we had kept silent >- MIStoppedEvent event = fSilencedSignalEvent.remove(fExecutionDmcToSuspend); >- if (event != null) { >- eventDispatched(event); >- } else { >- // Maybe the stopped event didn't arrive yet. >- // We don't want to silence it anymore >- fDisableNextSignalEventDmc.remove(fExecutionDmcToSuspend); >- } >- >- super.handleFailure(); >- } >- }); >- >- } else { >- // We didn't suspend the thread, so we don't need to resume it >- rm.done(); >- } >- } >- }; >- >- } >- /* ****************************************************************************** >- * End of section to support operations even when the target is unavailable. >- * ******************************************************************************/ >- >- /////////////////////////////////////////////////////////////////////////// >- // Event handlers >- /////////////////////////////////////////////////////////////////////////// >- >- /** >- * @nooverride This method is not intended to be re-implemented or extended by clients. >- * @noreference This method is not intended to be referenced by clients. >- */ >- @Override >- @DsfServiceEventHandler >- public void eventDispatched(final MIRunningEvent e) { >- if (fDisableNextRunningEventDmc.remove(e.getDMContext())) { >- // Don't broadcast the running event >- return; >- } >- getSession().dispatchEvent(new ResumedEvent(e.getDMContext(), e), getProperties()); >- } >- >- /** >- * @nooverride This method is not intended to be re-implemented or extended by clients. >- * @noreference This method is not intended to be referenced by clients. >- */ >- @Override >- @DsfServiceEventHandler >- public void eventDispatched(final MIStoppedEvent e) { >- if (getRunToLineActiveOperation() != null) { >- // First check if it is the right thread that stopped >- IMIExecutionDMContext threadDmc = DMContexts.getAncestorOfType(e.getDMContext(), IMIExecutionDMContext.class); >- if (getRunToLineActiveOperation().getThreadContext().equals(threadDmc)) { >- int bpId = 0; >- if (e instanceof MIBreakpointHitEvent) { >- bpId = ((MIBreakpointHitEvent)e).getNumber(); >- } >- String fileLocation = e.getFrame().getFile() + ":" + e.getFrame().getLine(); //$NON-NLS-1$ >- String addrLocation = e.getFrame().getAddress(); >- // Here we check three different things to see if we are stopped at the right place >- // 1- The actual location in the file. But this does not work for breakpoints that >- // were set on non-executable lines >- // 2- The address where the breakpoint was set. But this does not work for breakpoints >- // that have multiple addresses (GDB returns <MULTIPLE>.) I think that is for multi-process >- // 3- The breakpoint id that was hit. But this does not work if another breakpoint >- // was also set on the same line because GDB may return that breakpoint as being hit. >- // >- // So this works for the large majority of cases. The case that won't work is when the user >- // does a runToLine to a line that is non-executable AND has another breakpoint AND >- // has multiple addresses for the breakpoint. I'm mean, come on! >- if (fileLocation.equals(getRunToLineActiveOperation().getFileLocation()) || >- addrLocation.equals(getRunToLineActiveOperation().getAddrLocation()) || >- bpId == getRunToLineActiveOperation().getBreakointId()) { >- // We stopped at the right place. All is well. >- setRunToLineActiveOperation(null); >- } else { >- // The right thread stopped but not at the right place yet >- if (getRunToLineActiveOperation().shouldSkipBreakpoints() && e instanceof MIBreakpointHitEvent) { >- fConnection.queueCommand( >- fCommandFactory.createMIExecContinue(getRunToLineActiveOperation().getThreadContext()), >- new DataRequestMonitor<MIInfo>(getExecutor(), null)); >- >- // Don't send the stop event since we are resuming again. >- return; >- } else { >- // Stopped for any other reasons. Just remove our temporary one >- // since we don't want it to hit later >- // >- // Note that in Non-stop, we don't cancel a run-to-line when a new >- // breakpoint is inserted. This is because the new breakpoint could >- // be for another thread altogether and should not affect the current thread. >- IBreakpointsTargetDMContext bpDmc = DMContexts.getAncestorOfType(getRunToLineActiveOperation().getThreadContext(), >- IBreakpointsTargetDMContext.class); >- >- fConnection.queueCommand(fCommandFactory.createMIBreakDelete(bpDmc, new int[] {getRunToLineActiveOperation().getBreakointId()}), >- new DataRequestMonitor<MIInfo>(getExecutor(), null)); >- setRunToLineActiveOperation(null); >- } >- } >- } >- } >- >- IMIExecutionDMContext threadDmc = DMContexts.getAncestorOfType(e.getDMContext(), IMIExecutionDMContext.class); >- if (e instanceof MISignalEvent && fDisableNextSignalEventDmc.remove(threadDmc)) { >- fSilencedSignalEvent.put(threadDmc, e); >- >- // Don't broadcast the stopped event >- return; >- } >- >- IDMEvent<?> event = null; >- MIBreakpointDMContext bp = null; >- if (e instanceof MIBreakpointHitEvent) { >- int bpId = ((MIBreakpointHitEvent)e).getNumber(); >- IBreakpointsTargetDMContext bpsTarget = DMContexts.getAncestorOfType(e.getDMContext(), IBreakpointsTargetDMContext.class); >- if (bpsTarget != null && bpId >= 0) { >- bp = new MIBreakpointDMContext(getSession().getId(), new IDMContext[] {bpsTarget}, bpId); >- event = new BreakpointHitEvent(e.getDMContext(), (MIBreakpointHitEvent)e, bp); >- } >- } >- if (event == null) { >- event = new SuspendedEvent(e.getDMContext(), e); >- } >- >- getSession().dispatchEvent(event, getProperties()); >- } > } >Index: src/org/eclipse/cdt/dsf/gdb/service/GdbDebugServicesFactoryNS.java >=================================================================== >RCS file: /cvsroot/tools/org.eclipse.cdt/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GdbDebugServicesFactoryNS.java,v >retrieving revision 1.4 >diff -u -r1.4 GdbDebugServicesFactoryNS.java >--- src/org/eclipse/cdt/dsf/gdb/service/GdbDebugServicesFactoryNS.java 3 Apr 2011 02:15:11 -0000 1.4 >+++ src/org/eclipse/cdt/dsf/gdb/service/GdbDebugServicesFactoryNS.java 12 Apr 2011 00:38:59 -0000 >@@ -15,7 +15,7 @@ > > /** > * This variant is for non-stop (NS) multi-threaded debugging, a gdb capability >- * introduced in version 6.8.50. We provide a specialized NS implementation of >+ * introduced in version 7.0. We provide a specialized NS implementation of > * the run control service; that's the only specialization. > */ > public class GdbDebugServicesFactoryNS extends GdbDebugServicesFactory {
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Flags:
marc.khouzam
:
iplog-
Actions:
View
|
Diff
Attachments on
bug 337893
:
192028
|
192029
|
192038
|
192202
|
192418
|
192419
|
192606
| 192993 |
193212