| /******************************************************************************* |
| * Copyright (c) 2007, 2018 Wind River and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * Wind River - Initial API and implementation |
| * Ericsson - High-level breakpoints integration |
| * Ericsson - Added breakpoint filter support |
| * Ericsson - Re-factored the service and put a few comments |
| * Ericsson - Added Action support |
| * Marc Khouzam (Ericsson) - Fix support for thread filter (Bug 355833) |
| * Marc Khouzam (Ericsson) - Generalize thread filtering logic (Bug 431986) |
| * Marc Khouzam (Ericsson) - Accept multiple calls to startTrackingBreakpoints (Bug 389945) |
| * Marc Khouzam (Ericsson) - Support for dynamic printf (Bug 400628) |
| * Alvaro Sanchez-Leon (Ericsson) - Sometimes breakpoints set and immediately deleted when debugging with GDB (Bug 442394) |
| * Alvaro Sanchez-Leon (Ericsson) - Breakpoint Enable does not work after restarting the application (Bug 456959) |
| *******************************************************************************/ |
| |
| package org.eclipse.cdt.dsf.mi.service; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Hashtable; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.Vector; |
| import java.util.concurrent.RejectedExecutionException; |
| |
| import org.eclipse.cdt.core.model.ICModelMarker; |
| import org.eclipse.cdt.debug.core.CDebugCorePlugin; |
| import org.eclipse.cdt.debug.core.breakpointactions.BreakpointActionManager; |
| import org.eclipse.cdt.debug.core.model.ICAddressBreakpoint; |
| import org.eclipse.cdt.debug.core.model.ICBreakpoint; |
| import org.eclipse.cdt.debug.core.model.ICBreakpointExtension; |
| import org.eclipse.cdt.debug.core.model.ICBreakpointType; |
| import org.eclipse.cdt.debug.core.model.ICDynamicPrintf; |
| import org.eclipse.cdt.debug.core.model.ICEventBreakpoint; |
| import org.eclipse.cdt.debug.core.model.ICFunctionBreakpoint; |
| import org.eclipse.cdt.debug.core.model.ICLineBreakpoint; |
| import org.eclipse.cdt.debug.core.model.ICTracepoint; |
| import org.eclipse.cdt.debug.core.model.ICWatchpoint; |
| import org.eclipse.cdt.debug.core.model.ICWatchpoint2; |
| import org.eclipse.cdt.debug.internal.core.breakpoints.BreakpointProblems; |
| import org.eclipse.cdt.dsf.concurrent.CountingRequestMonitor; |
| import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; |
| import org.eclipse.cdt.dsf.concurrent.DsfRunnable; |
| import org.eclipse.cdt.dsf.concurrent.ImmediateExecutor; |
| import org.eclipse.cdt.dsf.concurrent.ImmediateRequestMonitor; |
| import org.eclipse.cdt.dsf.concurrent.RequestMonitor; |
| import org.eclipse.cdt.dsf.concurrent.ThreadSafe; |
| import org.eclipse.cdt.dsf.datamodel.DMContexts; |
| import org.eclipse.cdt.dsf.datamodel.IDMContext; |
| import org.eclipse.cdt.dsf.debug.service.IBreakpoints; |
| import org.eclipse.cdt.dsf.debug.service.IBreakpoints.IBreakpointDMContext; |
| import org.eclipse.cdt.dsf.debug.service.IBreakpoints.IBreakpointDMData; |
| import org.eclipse.cdt.dsf.debug.service.IBreakpoints.IBreakpointsTargetDMContext; |
| import org.eclipse.cdt.dsf.debug.service.IDsfBreakpointExtension; |
| import org.eclipse.cdt.dsf.debug.service.IProcesses; |
| import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext; |
| import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext; |
| import org.eclipse.cdt.dsf.debug.service.IRunControl.IExitedDMEvent; |
| import org.eclipse.cdt.dsf.debug.service.IRunControl.IStartedDMEvent; |
| import org.eclipse.cdt.dsf.debug.service.IRunControl.ISuspendedDMEvent; |
| import org.eclipse.cdt.dsf.debug.service.ISourceLookup; |
| import org.eclipse.cdt.dsf.debug.service.ISourceLookup.ISourceLookupDMContext; |
| import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService; |
| import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlShutdownDMEvent; |
| import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin; |
| import org.eclipse.cdt.dsf.mi.service.MIBreakpoints.BreakpointAddedEvent; |
| import org.eclipse.cdt.dsf.mi.service.MIBreakpoints.BreakpointRemovedEvent; |
| import org.eclipse.cdt.dsf.mi.service.MIBreakpoints.BreakpointUpdatedEvent; |
| import org.eclipse.cdt.dsf.mi.service.MIBreakpoints.MIBreakpointDMContext; |
| import org.eclipse.cdt.dsf.mi.service.MIRunControl.SuspendedEvent; |
| import org.eclipse.cdt.dsf.mi.service.breakpoint.actions.BreakpointActionAdapter; |
| import org.eclipse.cdt.dsf.mi.service.command.events.IMIDMEvent; |
| import org.eclipse.cdt.dsf.mi.service.command.events.MIBreakpointHitEvent; |
| import org.eclipse.cdt.dsf.mi.service.command.events.MIWatchpointScopeEvent; |
| import org.eclipse.cdt.dsf.mi.service.command.events.MIWatchpointTriggerEvent; |
| import org.eclipse.cdt.dsf.mi.service.command.output.MIBreakpoint; |
| import org.eclipse.cdt.dsf.service.AbstractDsfService; |
| import org.eclipse.cdt.dsf.service.DsfServiceEventHandler; |
| import org.eclipse.cdt.dsf.service.DsfSession; |
| import org.eclipse.cdt.gdb.internal.eventbkpts.GdbCatchpoints; |
| import org.eclipse.core.resources.IMarker; |
| import org.eclipse.core.resources.IMarkerDelta; |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.core.resources.IWorkspaceRunnable; |
| import org.eclipse.core.resources.ResourcesPlugin; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.ListenerList; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.core.runtime.jobs.ISchedulingRule; |
| import org.eclipse.core.runtime.jobs.Job; |
| import org.eclipse.core.runtime.jobs.MultiRule; |
| import org.eclipse.debug.core.DebugPlugin; |
| import org.eclipse.debug.core.IBreakpointListener; |
| import org.eclipse.debug.core.IBreakpointManager; |
| import org.eclipse.debug.core.IBreakpointManagerListener; |
| import org.eclipse.debug.core.model.IBreakpoint; |
| import org.osgi.framework.BundleContext; |
| |
| import com.ibm.icu.text.MessageFormat; |
| |
| /** |
| * Breakpoint service interface. The breakpoint service tracks CDT breakpoint |
| * objects, and based on those, it manages breakpoints in the debugger back end. |
| * |
| * It relies on MIBreakpoints for the actual back-end interface. |
| */ |
| public class MIBreakpointsManager extends AbstractDsfService |
| implements IBreakpointManagerListener, IBreakpointListener { |
| /** |
| * A listener is notified by {@link MIBreakpointsManager} when |
| * the breakpoints tracking starts or stops. |
| * @since 4.2 |
| */ |
| public interface IMIBreakpointsTrackingListener { |
| |
| public void breakpointTrackingStarted(IBreakpointsTargetDMContext bpTargetDMC); |
| |
| public void breakpointTrackingStopped(IBreakpointsTargetDMContext bpTargetDMC); |
| } |
| |
| // Note: Find a way to import this (careful of circular dependencies) |
| public static final String GDB_DEBUG_MODEL_ID = "org.eclipse.cdt.dsf.gdb"; //$NON-NLS-1$ |
| |
| // Extra breakpoint attributes |
| private static final String ATTR_DEBUGGER_PATH = GdbPlugin.PLUGIN_ID + ".debuggerPath"; //$NON-NLS-1$ |
| private static final String ATTR_THREAD_FILTER = GdbPlugin.PLUGIN_ID + ".threadFilter"; //$NON-NLS-1$ |
| private static final String ATTR_THREAD_ID = GdbPlugin.PLUGIN_ID + ".threadID"; //$NON-NLS-1$ |
| |
| // Services |
| private ICommandControlService fConnection; |
| private ISourceLookup fSourceLookup; |
| private IProcesses fProcesses; |
| private IBreakpoints fBreakpoints; |
| private IBreakpointManager fBreakpointManager; // Platform breakpoint manager (not this!) |
| private BreakpointActionManager fBreakpointActionManager; |
| |
| /////////////////////////////////////////////////////////////////////////// |
| // Breakpoints tracking |
| /////////////////////////////////////////////////////////////////////////// |
| |
| private String fDebugModelId; |
| |
| // Holds the set of platform breakpoints with their corresponding back-end |
| // breakpoint attributes, per context (i.e. each platform breakpoint is |
| // replicated for each execution context). |
| // - Context entry added/removed on start/stopTrackingBreakpoints() |
| // - Augmented on breakpointAdded() |
| // - Modified on breakpointChanged() |
| // - Diminished on breakpointRemoved() |
| private Map<IBreakpointsTargetDMContext, Map<ICBreakpoint, Map<String, Object>>> fPlatformToAttributesMaps = new HashMap<>(); |
| |
| /** |
| * Returns the structure that maps each breakpoint target to a map of platform breakpoints |
| * and their corresponding back-end attributes. |
| * @since 4.7 |
| */ |
| protected Map<IBreakpointsTargetDMContext, Map<ICBreakpoint, Map<String, Object>>> getPlatformToAttributesMaps() { |
| return fPlatformToAttributesMaps; |
| } |
| |
| // Holds the set of target breakpoints, per execution context, and their |
| // mapping to the corresponding platform breakpoint. In a given execution |
| // context there can only be one platform breakpoint per target breakpoint. |
| // Acts as a mapping from target (low-level) BP to the corresponding platform |
| // (high-level) BP. |
| // Updated when: |
| // - We start/stop tracking an execution context |
| // - A platform breakpoint is added/removed |
| // - A thread filter is applied/removed |
| private Map<IBreakpointsTargetDMContext, Map<IBreakpointDMContext, ICBreakpoint>> fBPToPlatformMaps = new HashMap<>(); |
| |
| /** |
| * Returns the structure that maps each breakpoint target to a map of back-end breakpoints |
| * and their corresponding platform breakpoint. |
| * @since 4.7 |
| */ |
| protected Map<IBreakpointsTargetDMContext, Map<IBreakpointDMContext, ICBreakpoint>> getBPToPlatformMaps() { |
| return fBPToPlatformMaps; |
| } |
| |
| // Holds the mapping from platform breakpoint to the corresponding target |
| // breakpoint(s), per context. There can be multiple back-end BPs for a |
| // single platform BP in the case of [1] multiple target contexts, and/or |
| // [2] thread filtering. |
| // Updated when: |
| // - We start/stop tracking an execution context |
| // - A platform breakpoint is added/removed |
| // - A thread filter is applied/removed |
| private Map<IBreakpointsTargetDMContext, Map<ICBreakpoint, Vector<IBreakpointDMContext>>> fPlatformToBPsMaps = new HashMap<>(); |
| |
| /** |
| * Returns the structure that maps each breakpoint target to a map of platform breakpoints |
| * and their corresponding vector of back-end breakpoints. |
| * @since 4.7 |
| */ |
| protected Map<IBreakpointsTargetDMContext, Map<ICBreakpoint, Vector<IBreakpointDMContext>>> getPlatformToBPsMaps() { |
| return fPlatformToBPsMaps; |
| } |
| |
| // Holds the mapping from platform breakpoint to the corresponding target |
| // breakpoint threads, per context. |
| // Updated when: |
| // - We start/stop tracking an execution context |
| // - A platform breakpoint is added/removed |
| // - A thread filter is applied/removed |
| private Map<IBreakpointsTargetDMContext, Map<ICBreakpoint, Set<String>>> fPlatformToBPThreadsMaps = new HashMap<>(); |
| |
| /** |
| * Returns the structure that maps each breakpoint target to a map of platform breakpoints |
| * and their corresponding back-end breakpoint thread ids. |
| * @since 4.7 |
| */ |
| protected Map<IBreakpointsTargetDMContext, Map<ICBreakpoint, Set<String>>> getPlatformToBPThreadsMaps() { |
| return fPlatformToBPThreadsMaps; |
| } |
| |
| // Due to the very asynchronous nature of DSF, a new breakpoint request can |
| // pop up at any time before an ongoing one is completed. The following set |
| // is used to store requests until the ongoing operation completes. |
| private Set<IBreakpoint> fPendingRequests = new HashSet<>(); |
| private Set<IBreakpoint> fPendingBreakpoints = new HashSet<>(); |
| |
| private Map<ICBreakpoint, IMarker> fBreakpointMarkerProblems = new HashMap<>(); |
| |
| private ListenerList<IMIBreakpointsTrackingListener> fTrackingListeners = new ListenerList<>(); |
| |
| /////////////////////////////////////////////////////////////////////////// |
| // String constants |
| /////////////////////////////////////////////////////////////////////////// |
| |
| private static final String NULL_STRING = ""; //$NON-NLS-1$ |
| |
| static final String CONTEXT_ALREADY_INITIALIZED = "Context already initialized"; //$NON-NLS-1$ |
| static final String INVALID_CONTEXT_TYPE = "Invalid context type"; //$NON-NLS-1$ |
| static final String INVALID_CONTEXT = "Invalid context"; //$NON-NLS-1$ |
| |
| static final String UNABLE_TO_READ_BREAKPOINT = "Unable to read initial breakpoint attributes"; //$NON-NLS-1$ |
| static final String BREAKPOINT_NOT_INSTALLED = "Breakpoints not installed for given context"; //$NON-NLS-1$ |
| static final String BREAKPOINT_ALREADY_INSTALLED = "Breakpoint already installed"; //$NON-NLS-1$ |
| static final String BREAKPOINT_ALREADY_REMOVED = "Breakpoint already removed"; //$NON-NLS-1$ |
| |
| static final String INVALID_BREAKPOINT = "Invalid breakpoint"; //$NON-NLS-1$ |
| static final String UNKNOWN_BREAKPOINT = "Unknown breakpoint"; //$NON-NLS-1$ |
| static final String INVALID_PARAMETER = "Invalid breakpoint parameter(s)"; //$NON-NLS-1$ |
| |
| static final String NO_DEBUGGER_PATH = "No debugger path for breakpoint"; //$NON-NLS-1$ |
| static final String NO_MARKER_FOR_BREAKPOINT = "No marker associated with breakpoint"; //$NON-NLS-1$ |
| |
| /////////////////////////////////////////////////////////////////////////// |
| // AbstractDsfService |
| /////////////////////////////////////////////////////////////////////////// |
| |
| /** |
| * The service constructor. |
| * Performs basic instantiation (method initialize() performs the real |
| * service initialization asynchronously). |
| * |
| * @param session the debugging session |
| * @param debugModelId the debugging model |
| */ |
| public MIBreakpointsManager(DsfSession session, String debugModelId) { |
| super(session); |
| fDebugModelId = debugModelId; |
| } |
| |
| //------------------------------------------------------------------------- |
| // initialize |
| //------------------------------------------------------------------------- |
| // - Collect references for the services we interact with |
| // - Register to interesting events |
| // - Obtain the list of platform breakpoints |
| // - Register the service for interested parties |
| //------------------------------------------------------------------------- |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.cdt.dsf.service.AbstractDsfService#initialize(org.eclipse.cdt.dsf.concurrent.RequestMonitor) |
| */ |
| @Override |
| public void initialize(final RequestMonitor rm) { |
| super.initialize(new ImmediateRequestMonitor(rm) { |
| @Override |
| protected void handleSuccess() { |
| doInitialize(rm); |
| } |
| }); |
| } |
| |
| /** |
| * @param rm |
| */ |
| private void doInitialize(RequestMonitor rm) { |
| |
| // Get the required services references from central repository |
| fConnection = getServicesTracker().getService(ICommandControlService.class); |
| fSourceLookup = getServicesTracker().getService(ISourceLookup.class); |
| fBreakpoints = getServicesTracker().getService(IBreakpoints.class); |
| fProcesses = getServicesTracker().getService(IProcesses.class); |
| fBreakpointManager = DebugPlugin.getDefault().getBreakpointManager(); |
| fBreakpointActionManager = CDebugCorePlugin.getDefault().getBreakpointActionManager(); |
| |
| // Register to the useful events |
| getSession().addServiceEventListener(this, null); |
| fBreakpointManager.addBreakpointListener(this); |
| fBreakpointManager.addBreakpointManagerListener(this); |
| |
| // And register this service |
| register(new String[] { MIBreakpointsManager.class.getName() }, new Hashtable<String, String>()); |
| rm.done(); |
| } |
| |
| //------------------------------------------------------------------------- |
| // shutdown |
| //------------------------------------------------------------------------- |
| // - Un-register the service |
| // - Stop listening to events |
| // - Remove the breakpoints installed by this service |
| // |
| // Since we are shutting down, there is no overwhelming need |
| // to keep the maps coherent... |
| //------------------------------------------------------------------------- |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.cdt.dsf.service.AbstractDsfService#shutdown(org.eclipse.cdt.dsf.concurrent.RequestMonitor) |
| */ |
| @Override |
| public void shutdown(final RequestMonitor rm) { |
| |
| // Stop accepting requests and events |
| unregister(); |
| getSession().removeServiceEventListener(this); |
| fBreakpointManager.removeBreakpointListener(this); |
| fBreakpointManager.removeBreakpointManagerListener(this); |
| fTrackingListeners.clear(); |
| |
| // Cleanup the breakpoints that are still installed by the service. |
| // Use a counting monitor which will call mom to complete the shutdown |
| // after the breakpoints are un-installed (successfully or not). |
| CountingRequestMonitor countingRm = new CountingRequestMonitor(getExecutor(), rm) { |
| @Override |
| protected void handleCompleted() { |
| MIBreakpointsManager.super.shutdown(rm); |
| } |
| }; |
| |
| List<IBreakpointsTargetDMContext> targetBPKeys = new ArrayList<>(fBPToPlatformMaps.size()); |
| targetBPKeys.addAll(0, fBPToPlatformMaps.keySet()); |
| for (IBreakpointsTargetDMContext dmc : targetBPKeys) { |
| stopTrackingBreakpoints(dmc, countingRm); |
| } |
| countingRm.setDoneCount(targetBPKeys.size()); |
| } |
| |
| //------------------------------------------------------------------------- |
| // getBundleContext |
| //------------------------------------------------------------------------- |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.cdt.dsf.service.AbstractDsfService#getBundleContext() |
| */ |
| @Override |
| protected BundleContext getBundleContext() { |
| return GdbPlugin.getBundleContext(); |
| } |
| |
| /////////////////////////////////////////////////////////////////////////// |
| // IBreakpointsManager |
| /////////////////////////////////////////////////////////////////////////// |
| |
| /** |
| * Wrapper around startTrackingBreakpoints() which accepts a containerDmc and sets |
| * each breakpoints filter to include that container. This method should be called |
| * instead of startTrackingBreakpoints() |
| * |
| * @param containerDmc The container to be added in the bp filter. This container |
| * must have the proper IBreakpointsTargetDMContext in its hierarchy. |
| * |
| * @since 4.6 |
| */ |
| public void startTrackingBpForProcess(final IContainerDMContext containerDmc, final RequestMonitor rm) { |
| final IBreakpointsTargetDMContext targetBpDmc = DMContexts.getAncestorOfType(containerDmc, |
| IBreakpointsTargetDMContext.class); |
| |
| IBreakpoint[] breakpoints = fBreakpointManager.getBreakpoints(fDebugModelId); |
| for (IBreakpoint breakpoint : breakpoints) { |
| if (breakpoint instanceof ICBreakpoint && supportsBreakpoint(breakpoint)) { |
| setTargetFilter((ICBreakpoint) breakpoint, containerDmc); |
| } |
| } |
| startTrackingBreakpoints(targetBpDmc, rm); |
| } |
| |
| //------------------------------------------------------------------------- |
| // startTrackingBreakpoints |
| //------------------------------------------------------------------------- |
| // - Augment the maps with the new execution context |
| // - Install the platform breakpoints on the selected target |
| //------------------------------------------------------------------------- |
| |
| /** |
| * Install and begin tracking breakpoints for given context. The service |
| * will keep installing new breakpoints that appear in the IDE for this |
| * context until {@link #uninstallBreakpoints(IDMContext)} is called for that |
| * context. |
| * @param dmc Context to start tracking breakpoints for. |
| * @param rm Completion callback. |
| */ |
| public void startTrackingBreakpoints(final IBreakpointsTargetDMContext dmc, final RequestMonitor rm) { |
| |
| // Validate the execution context |
| if (dmc == null) { |
| rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, INVALID_CONTEXT, null)); |
| rm.done(); |
| return; |
| } |
| |
| Map<ICBreakpoint, Map<String, Object>> platformBPs = fPlatformToAttributesMaps.get(dmc); |
| Map<ICBreakpoint, Vector<IBreakpointDMContext>> breakpointIDs = fPlatformToBPsMaps.get(dmc); |
| Map<IBreakpointDMContext, ICBreakpoint> targetIDs = fBPToPlatformMaps.get(dmc); |
| Map<ICBreakpoint, Set<String>> threadIDs = fPlatformToBPThreadsMaps.get(dmc); |
| if ((platformBPs != null) || (breakpointIDs != null) || (targetIDs != null) || (threadIDs != null)) { |
| // If the maps already contains this context we can simply ignore this request. |
| // This happens when we start or attach to another process with GDB >= 7.4 |
| assert platformBPs != null && breakpointIDs != null && targetIDs != null && threadIDs != null; |
| rm.done(); |
| return; |
| } |
| |
| // Create entries in the breakpoint tables for the new context. These entries should only |
| // be removed when this service stops tracking breakpoints for the given context. |
| fPlatformToAttributesMaps.put(dmc, new HashMap<ICBreakpoint, Map<String, Object>>()); |
| fPlatformToBPsMaps.put(dmc, new HashMap<ICBreakpoint, Vector<IBreakpointDMContext>>()); |
| fBPToPlatformMaps.put(dmc, new HashMap<IBreakpointDMContext, ICBreakpoint>()); |
| fPlatformToBPThreadsMaps.put(dmc, new HashMap<ICBreakpoint, Set<String>>()); |
| |
| // Install the platform breakpoints (stored in fPlatformBPs) on the target. |
| new Job("DSF BreakpointsManager: Install initial breakpoints on target") { //$NON-NLS-1$ |
| @Override |
| protected IStatus run(IProgressMonitor monitor) { |
| // Submit the runnable to plant the breakpoints on dispatch thread. |
| getExecutor().submit( |
| () -> installInitialBreakpoints(dmc, new RequestMonitor(ImmediateExecutor.getInstance(), rm) { |
| @Override |
| protected void handleSuccess() { |
| // Notify breakpoints tracking listeners that the tracking is started. |
| for (Object o : fTrackingListeners.getListeners()) { |
| ((IMIBreakpointsTrackingListener) o).breakpointTrackingStarted(dmc); |
| } |
| rm.done(); |
| } |
| })); |
| |
| return Status.OK_STATUS; |
| } |
| }.schedule(); |
| } |
| |
| /** |
| * Installs the breakpoints that existed prior to the activation of this |
| * execution context. |
| * |
| * @param dmc |
| * @param initialPlatformBPs |
| * @param rm |
| */ |
| private void installInitialBreakpoints(final IBreakpointsTargetDMContext dmc, final RequestMonitor rm) { |
| // Retrieve the set of platform breakpoints for this context |
| final Map<ICBreakpoint, Map<String, Object>> platformBPs = fPlatformToAttributesMaps.get(dmc); |
| if (platformBPs == null) { |
| rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, INVALID_CONTEXT, null)); |
| rm.done(); |
| return; |
| } |
| |
| // Read current breakpoints from platform and copy their augmented |
| // attributes into the local reference map |
| try { |
| IBreakpoint[] breakpoints = fBreakpointManager.getBreakpoints(fDebugModelId); |
| for (IBreakpoint breakpoint : breakpoints) { |
| if (supportsBreakpoint(breakpoint)) { |
| boolean filtered = isBreakpointEntirelyFiltered(dmc, (ICBreakpoint) breakpoint); |
| if (!filtered) { |
| Map<String, Object> attributes = breakpoint.getMarker().getAttributes(); |
| attributes.put(ATTR_DEBUGGER_PATH, NULL_STRING); |
| attributes.put(ATTR_THREAD_FILTER, extractThreads(dmc, (ICBreakpoint) breakpoint)); |
| attributes.put(ATTR_THREAD_ID, NULL_STRING); |
| platformBPs.put((ICBreakpoint) breakpoint, attributes); |
| } |
| } |
| } |
| } catch (CoreException e) { |
| IStatus status = new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNABLE_TO_READ_BREAKPOINT, |
| e); |
| rm.setStatus(status); |
| rm.done(); |
| } |
| |
| // Install the individual breakpoints on the dispatcher thread |
| // Requires a counting monitor to know when we are done |
| final CountingRequestMonitor countingRm = new CountingRequestMonitor(getExecutor(), rm); |
| countingRm.setDoneCount(platformBPs.size()); |
| |
| for (final ICBreakpoint breakpoint : platformBPs.keySet()) { |
| final Map<String, Object> attributes = platformBPs.get(breakpoint); |
| // Upon determining the debuggerPath, the breakpoint is installed |
| determineDebuggerPath(dmc, attributes, new RequestMonitor(getExecutor(), countingRm) { |
| @Override |
| protected void handleSuccess() { |
| // Must install breakpoints right away, even if disabled, so that |
| // we can find out if they apply to this target (Bug 389070) |
| installBreakpoint(dmc, breakpoint, attributes, countingRm); |
| } |
| }); |
| } |
| } |
| |
| //------------------------------------------------------------------------- |
| // stopTrackingBreakpoints |
| //------------------------------------------------------------------------- |
| // - Remove the target breakpoints for the given execution context |
| // - Update the maps |
| //------------------------------------------------------------------------- |
| |
| /** |
| * Uninstall and stop tracking breakpoints for the given context. |
| * @param dmc Context to start tracking breakpoints for. |
| * @param rm Completion callback. |
| */ |
| public void stopTrackingBreakpoints(final IBreakpointsTargetDMContext dmc, final RequestMonitor rm) { |
| |
| // Validate the context |
| if (dmc == null) { |
| rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, INVALID_CONTEXT, null)); |
| rm.done(); |
| return; |
| } |
| |
| // Retrieve the set of platform breakpoints for this context |
| final Map<ICBreakpoint, Map<String, Object>> platformBPs = fPlatformToAttributesMaps.get(dmc); |
| if (platformBPs == null) { |
| rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, INVALID_CONTEXT, null)); |
| rm.done(); |
| return; |
| } |
| |
| // Un-install the individual breakpoints on the dispatcher thread |
| // (requires a counting monitor to know when we are done). |
| // On completion (success or failure), update the maps. |
| final CountingRequestMonitor countingRm = new CountingRequestMonitor(getExecutor(), rm) { |
| @Override |
| protected void handleCompleted() { |
| fPlatformToAttributesMaps.remove(dmc); |
| fPlatformToBPsMaps.remove(dmc); |
| fBPToPlatformMaps.remove(dmc); |
| fPlatformToBPThreadsMaps.remove(dmc); |
| // Notify breakpoints tracking listeners that the tracking is stopped. |
| for (IMIBreakpointsTrackingListener o : fTrackingListeners) { |
| o.breakpointTrackingStopped(dmc); |
| } |
| rm.done(); |
| } |
| }; |
| countingRm.setDoneCount(platformBPs.size()); |
| |
| for (final ICBreakpoint breakpoint : platformBPs.keySet()) { |
| uninstallBreakpoint(dmc, breakpoint, new RequestMonitor(getExecutor(), countingRm) { |
| @Override |
| protected void handleCompleted() { |
| countingRm.done(); |
| } |
| }); |
| } |
| } |
| |
| /** |
| * Return the collection of tracked target breakpoint contexts. Use this method |
| * instead of implying the installed collection from the various maps contained |
| * in the manager. |
| * |
| * @since 5.5 |
| */ |
| protected Collection<IBreakpointsTargetDMContext> getTrackedBreakpointTargetContexts() { |
| return fPlatformToAttributesMaps.keySet(); |
| } |
| |
| /////////////////////////////////////////////////////////////////////////// |
| // Back-end interface functions |
| /////////////////////////////////////////////////////////////////////////// |
| |
| //------------------------------------------------------------------------- |
| // installBreakpoint |
| //------------------------------------------------------------------------- |
| |
| /** |
| * Install a platform breakpoint on the back-end. For a given context, a |
| * platform breakpoint can resolve into multiple back-end breakpoints when |
| * threads are taken into account or if multiple breakpoints are created |
| * on the target using the console. |
| * |
| * @param dmc |
| * @param breakpoint |
| * @param attributes |
| * @param rm |
| */ |
| private void installBreakpoint(IBreakpointsTargetDMContext dmc, final ICBreakpoint breakpoint, |
| final Map<String, Object> attributes, final RequestMonitor rm) { |
| // Retrieve the breakpoint maps |
| final Map<ICBreakpoint, Map<String, Object>> platformBPs = fPlatformToAttributesMaps.get(dmc); |
| assert platformBPs != null; |
| |
| final Map<ICBreakpoint, Vector<IBreakpointDMContext>> breakpointIDs = fPlatformToBPsMaps.get(dmc); |
| assert breakpointIDs != null; |
| |
| final Map<IBreakpointDMContext, ICBreakpoint> targetBPs = fBPToPlatformMaps.get(dmc); |
| assert targetBPs != null; |
| |
| final Map<ICBreakpoint, Set<String>> threadsIDs = fPlatformToBPThreadsMaps.get(dmc); |
| assert threadsIDs != null; |
| |
| // Ensure the breakpoint has a valid debugger source path |
| if (breakpoint instanceof ICLineBreakpoint && !(breakpoint instanceof ICAddressBreakpoint) |
| && !(breakpoint instanceof ICFunctionBreakpoint)) { |
| String debuggerPath = (String) attributes.get(ATTR_DEBUGGER_PATH); |
| if (debuggerPath == null || debuggerPath.equals(NULL_STRING)) { |
| rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, NO_DEBUGGER_PATH, null)); |
| rm.done(); |
| return; |
| } |
| } |
| |
| // A back-end breakpoint needs to be installed for each specified thread |
| final Set<String> threads = getThreads(attributes); |
| |
| // Update the breakpoint state when all back-end breakpoints have been installed |
| final CountingRequestMonitor installRM = new CountingRequestMonitor(getExecutor(), rm) { |
| @Override |
| protected void handleCompleted() { |
| // Store the platform breakpoint |
| platformBPs.put(breakpoint, attributes); |
| rm.done(); |
| } |
| }; |
| installRM.setDoneCount(threads.size()); |
| |
| // Install the back-end breakpoint(s) |
| for (final String thread : threads) { |
| DataRequestMonitor<IBreakpointDMContext> drm = new DataRequestMonitor<IBreakpointDMContext>(getExecutor(), |
| installRM) { |
| @Override |
| protected void handleSuccess() { |
| // Add the new back-end breakpoint to the map |
| Vector<IBreakpointDMContext> list = breakpointIDs.get(breakpoint); |
| if (list == null) { |
| list = new Vector<>(); |
| } |
| |
| IBreakpointDMContext targetBP = getData(); |
| list.add(targetBP); |
| breakpointIDs.put(breakpoint, list); |
| |
| // Add the reverse mapping |
| targetBPs.put(targetBP, breakpoint); |
| |
| // And update the corresponding thread list |
| Set<String> thrds = threadsIDs.get(breakpoint); |
| if (thrds == null) { |
| thrds = new HashSet<>(); |
| } |
| |
| thrds.add(thread); |
| threadsIDs.put(breakpoint, thrds); |
| |
| // Reset the thread (is it necessary?) |
| attributes.put(ATTR_THREAD_ID, NULL_STRING); |
| |
| // Remove breakpoint problem marker (if any) |
| removeBreakpointProblemMarker(breakpoint); |
| |
| // Check for a pending breakpoint before showing that it was properly installed |
| fBreakpoints.getBreakpointDMData(targetBP, |
| new DataRequestMonitor<IBreakpointDMData>(getExecutor(), null) { |
| @Override |
| protected void handleCompleted() { |
| boolean pending = false; |
| if (isSuccess()) { |
| IBreakpointDMData data = getData(); |
| if (data instanceof MIBreakpointDMData) { |
| pending = ((MIBreakpointDMData) data).isPending(); |
| } |
| } |
| // Finally, update the platform breakpoint to show it was installed, unless we have a pending breakpoint |
| if (!pending) { |
| try { |
| breakpoint.incrementInstallCount(); |
| } catch (CoreException e) { |
| } |
| } |
| installRM.done(); |
| } |
| }); |
| } |
| |
| @Override |
| protected void handleError() { |
| String detailedMessage; |
| if (getStatus().getException() != null && getStatus().getException().getMessage() != null) { |
| detailedMessage = getStatus().getException().getMessage(); |
| } else { |
| detailedMessage = getStatus().getMessage(); |
| } |
| String description = (detailedMessage == null) ? Messages.Breakpoint_attribute_problem |
| : MessageFormat.format(Messages.Breakpoint_attribute_detailed_problem, |
| new Object[] { detailedMessage }); |
| |
| addBreakpointProblemMarker(breakpoint, description, IMarker.SEVERITY_WARNING); |
| installRM.done(); |
| } |
| }; |
| |
| // Convert the breakpoint attributes for the back-end |
| attributes.put(ATTR_THREAD_ID, thread); |
| Map<String, Object> targetAttributes = convertToTargetBreakpoint(breakpoint, attributes); |
| // Must install breakpoint right away, even if disabled, so that |
| // we can find out if it applies to this target (Bug 389070) |
| fBreakpoints.insertBreakpoint(dmc, targetAttributes, drm); |
| } |
| } |
| |
| private void addBreakpointProblemMarker(final ICBreakpoint breakpoint, final String description, |
| final int severity) { |
| |
| new Job("Add Breakpoint Problem Marker") { //$NON-NLS-1$ |
| @Override |
| protected IStatus run(IProgressMonitor monitor) { |
| |
| if (breakpoint instanceof ICLineBreakpoint) { |
| // If we have already have a problem marker on this breakpoint |
| // we should remove it first. |
| IMarker marker = fBreakpointMarkerProblems.remove(breakpoint); |
| if (marker != null) { |
| try { |
| marker.delete(); |
| } catch (CoreException e) { |
| } |
| } |
| |
| ICLineBreakpoint lineBreakpoint = (ICLineBreakpoint) breakpoint; |
| try { |
| // Locate the workspace resource via the breakpoint marker |
| IMarker breakpointMarker = lineBreakpoint.getMarker(); |
| IResource resource = breakpointMarker.getResource(); |
| |
| // Add a problem marker to the resource |
| IMarker problemMarker = resource.createMarker(BreakpointProblems.BREAKPOINT_PROBLEM_MARKER_ID); |
| int lineNumber = lineBreakpoint.getLineNumber(); |
| String sourceHandle = lineBreakpoint.getSourceHandle(); |
| problemMarker.setAttribute(IMarker.LOCATION, String.valueOf(lineNumber)); |
| problemMarker.setAttribute(IMarker.MESSAGE, description); |
| problemMarker.setAttribute(IMarker.SEVERITY, severity); |
| problemMarker.setAttribute(IMarker.LINE_NUMBER, lineNumber); |
| if (sourceHandle != null) { |
| problemMarker.setAttribute(ICModelMarker.C_MODEL_MARKER_EXTERNAL_LOCATION, sourceHandle); |
| } |
| |
| // And save the baby |
| fBreakpointMarkerProblems.put(breakpoint, problemMarker); |
| } catch (CoreException e) { |
| } |
| } |
| return Status.OK_STATUS; |
| } |
| }.schedule(); |
| } |
| |
| private void removeBreakpointProblemMarker(final ICBreakpoint breakpoint) { |
| |
| new Job("Remove Breakpoint Problem Marker") { //$NON-NLS-1$ |
| @Override |
| protected IStatus run(IProgressMonitor monitor) { |
| |
| IMarker marker = fBreakpointMarkerProblems.remove(breakpoint); |
| if (marker != null) { |
| try { |
| marker.delete(); |
| } catch (CoreException e) { |
| } |
| } |
| |
| return Status.OK_STATUS; |
| } |
| }.schedule(); |
| } |
| |
| //------------------------------------------------------------------------- |
| // uninstallBreakpoint |
| //------------------------------------------------------------------------- |
| |
| /** |
| * Un-install an individual breakpoint on the back-end. For one platform |
| * breakpoint in a given execution context, there could be multiple |
| * corresponding back-end breakpoints (one per thread). |
| * |
| * @param dmc |
| * @param breakpoint |
| * @param rm |
| * |
| * @since 4.2 |
| */ |
| public void uninstallBreakpoint(final IBreakpointsTargetDMContext dmc, final ICBreakpoint breakpoint, |
| RequestMonitor rm) { |
| // Remove all relevant target filters |
| // Note that this call is important if a breakpoint is removed directly |
| // from the gdb console, or else we will try to re-install it (bug 433044) |
| removeAllTargetFilters(dmc, breakpoint); |
| |
| // Remove breakpoint problem marker (if any) |
| removeBreakpointProblemMarker(breakpoint); |
| |
| doUninstallBreakpoint(dmc, breakpoint, new ImmediateRequestMonitor(rm) { |
| @Override |
| protected void handleSuccess() { |
| Map<ICBreakpoint, Map<String, Object>> platformBPs = fPlatformToAttributesMaps.get(dmc); |
| // Note: Protect against NPE. It looks like we just checked the "platformBPs" is not null, |
| // in doUninstallBreakpoint(), but there is a race condition that can make it null |
| // in the meantime, if the debug session is destroyed. See bug 511727 |
| if (platformBPs != null) { |
| platformBPs.remove(breakpoint); |
| } |
| super.handleSuccess(); |
| } |
| }); |
| } |
| |
| /* |
| * Un-install the target breakpoints associated with the given platform breakpoint. |
| * The information related to the platform breakpoints is not cleared from the session. |
| * This allows the target breakpoints to be re-installed when an attribute of the platform |
| * breakpoint is changed. |
| */ |
| private void doUninstallBreakpoint(final IBreakpointsTargetDMContext dmc, final ICBreakpoint breakpoint, |
| final RequestMonitor rm) { |
| // Retrieve the breakpoint maps |
| final Map<ICBreakpoint, Map<String, Object>> platformBPs = fPlatformToAttributesMaps.get(dmc); |
| assert platformBPs != null; |
| |
| final Map<ICBreakpoint, Vector<IBreakpointDMContext>> breakpointIDs = fPlatformToBPsMaps.get(dmc); |
| assert breakpointIDs != null; |
| |
| final Map<IBreakpointDMContext, ICBreakpoint> targetBPs = fBPToPlatformMaps.get(dmc); |
| assert targetBPs != null; |
| |
| final Map<ICBreakpoint, Set<String>> threadsIDs = fPlatformToBPThreadsMaps.get(dmc); |
| assert threadsIDs != null; |
| |
| // Minimal validation |
| if (!platformBPs.containsKey(breakpoint) || !breakpointIDs.containsKey(breakpoint) |
| || !targetBPs.containsValue(breakpoint)) { |
| rm.setStatus( |
| new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, BREAKPOINT_ALREADY_REMOVED, null)); |
| rm.done(); |
| return; |
| } |
| |
| // Remove completion monitor |
| // Upon completion, update the mappings |
| final CountingRequestMonitor removeRM = new CountingRequestMonitor(getExecutor(), rm) { |
| @Override |
| protected void handleSuccess() { |
| // Update the mappings |
| threadsIDs.remove(breakpoint); |
| fPendingRequests.remove(breakpoint); |
| rm.done(); |
| } |
| }; |
| |
| // Remove the back-end breakpoints |
| // Remove the entry from the breakpointIDs map right away to indicate that we have already |
| // taken care of this breakpoint. This avoids race conditions with the shutdown |
| // which would also try to decrement the install count (bug 344635) |
| Vector<IBreakpointDMContext> list = breakpointIDs.remove(breakpoint); |
| int count = 0; |
| if (list != null) { |
| for (final IBreakpointDMContext bp : list) { |
| targetBPs.remove(bp); |
| decrementInstallCount(bp, breakpoint, new RequestMonitor(getExecutor(), removeRM) { |
| @Override |
| protected void handleCompleted() { |
| fBreakpoints.removeBreakpoint(bp, removeRM); |
| } |
| }); |
| } |
| count = list.size(); |
| list.clear(); // probably not necessary |
| } |
| removeRM.setDoneCount(count); |
| } |
| |
| private void decrementInstallCount(IBreakpointDMContext targetDmc, final ICBreakpoint breakpoint, |
| final RequestMonitor rm) { |
| fBreakpoints.getBreakpointDMData(targetDmc, new DataRequestMonitor<IBreakpointDMData>(getExecutor(), rm) { |
| @Override |
| protected void handleCompleted() { |
| boolean pending = false; |
| if (isSuccess()) { |
| IBreakpointDMData data = getData(); |
| if (data instanceof MIBreakpointDMData) { |
| pending = ((MIBreakpointDMData) data).isPending(); |
| } |
| } |
| // Finally, update the platform breakpoint to show it was un-installed. |
| // But we don't do this for pending breakpoints since they were |
| // not marked as installed. |
| if (!pending) { |
| try { |
| breakpoint.decrementInstallCount(); |
| } catch (CoreException e) { |
| } |
| } |
| rm.done(); |
| } |
| }); |
| } |
| //------------------------------------------------------------------------- |
| // modifyBreakpoint |
| //------------------------------------------------------------------------- |
| |
| /** |
| * Modify a platform breakpoint which can translate to quite a few updates |
| * on the target... |
| * |
| * @param dmc |
| * @param breakpoint |
| * @param attributes |
| * @param oldValues |
| * @param rm |
| */ |
| private void modifyBreakpoint(final IBreakpointsTargetDMContext dmc, final ICBreakpoint breakpoint, |
| final Map<String, Object> attributes, final IMarkerDelta oldValues, final RequestMonitor rm) { |
| // Retrieve the breakpoint maps |
| final Map<ICBreakpoint, Map<String, Object>> platformBPs = fPlatformToAttributesMaps.get(dmc); |
| assert platformBPs != null; |
| |
| final Map<ICBreakpoint, Vector<IBreakpointDMContext>> breakpointIDs = fPlatformToBPsMaps.get(dmc); |
| assert breakpointIDs != null; |
| |
| final Map<IBreakpointDMContext, ICBreakpoint> targetBPs = fBPToPlatformMaps.get(dmc); |
| assert targetBPs != null; |
| |
| final Map<ICBreakpoint, Set<String>> threadsIDs = fPlatformToBPThreadsMaps.get(dmc); |
| assert threadsIDs != null; |
| |
| boolean filtered = isBreakpointEntirelyFiltered(dmc, breakpoint); |
| |
| if (filtered && !platformBPs.containsKey(breakpoint)) { |
| rm.done(); |
| return; |
| } |
| |
| // Check if the breakpoint is installed: |
| // the installation might have failed; in this case, we try to install it again because |
| // some attribute might have changed which will make the install succeed. |
| if (!breakpointIDs.containsKey(breakpoint) && !targetBPs.containsValue(breakpoint)) { |
| if (!filtered) { |
| // Do not try to re-install the breakpoint if the change event is the result of changes |
| // in the breakpoint's install count. |
| // Excluding ATTR_DEBUGGER_PATH from the comparison because it has been set just before |
| // "modifyBreakpoint()" was called. |
| // (Bugzilla 534309) Guard against NULL oldValues, which is legitimate, in which case use an empty Map. |
| String[] diff = compareAttributes( |
| oldValues == null ? Collections.emptyMap() : oldValues.getAttributes(), attributes, |
| new String[] { ATTR_DEBUGGER_PATH }); |
| if (diff.length != 1 || !diff[0].equals(ICBreakpoint.INSTALL_COUNT)) { |
| attributes.put(ATTR_DEBUGGER_PATH, NULL_STRING); |
| attributes.put(ATTR_THREAD_FILTER, extractThreads(dmc, breakpoint)); |
| attributes.put(ATTR_THREAD_ID, NULL_STRING); |
| determineDebuggerPath(dmc, attributes, new RequestMonitor(getExecutor(), rm) { |
| @Override |
| protected void handleSuccess() { |
| installBreakpoint(dmc, breakpoint, attributes, rm); |
| } |
| }); |
| } else { |
| rm.done(); |
| } |
| } else { |
| rm.done(); |
| } |
| return; |
| } |
| |
| if (filtered) { |
| uninstallBreakpoint(dmc, breakpoint, rm); |
| return; |
| } |
| |
| // Get the original breakpoint attributes |
| final Map<String, Object> originalAttributes = platformBPs.get(breakpoint); |
| if (originalAttributes == null) { |
| rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, INVALID_BREAKPOINT, null)); |
| rm.done(); |
| return; |
| } |
| |
| // Determine the attributes delta |
| final Map<String, Object> oldAttributes = new HashMap<>(originalAttributes); |
| oldAttributes.put(ATTR_THREAD_FILTER, threadsIDs.get(breakpoint)); |
| |
| final Set<String> newThreads = extractThreads(dmc, breakpoint); |
| Map<String, Object> newAttributes = new HashMap<>(attributes); |
| newAttributes.put(ATTR_THREAD_FILTER, newThreads); |
| |
| final Map<String, Object> attributesDelta = determineAttributesDelta(oldAttributes, newAttributes); |
| |
| // Get the list of back-end breakpoints |
| final Vector<IBreakpointDMContext> oldTargetBPs = new Vector<>(breakpointIDs.get(breakpoint)); |
| if (oldTargetBPs.isEmpty()) { |
| rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, INVALID_BREAKPOINT, null)); |
| rm.done(); |
| return; |
| } |
| |
| // We're all set for the breakpoint update. |
| // |
| // The path for a simple update is straightforward: |
| // - For each back-end BP corresponding to a platform BP |
| // - Send an update command to the back-end |
| // - If the operation succeeded, update the data structures |
| // - If the operation failed, try to roll-back |
| // |
| // In cases where the the back-end breakpoint cannot be |
| // simply updated (e.g. thread filter modification), the old |
| // breakpoint has to be removed and new one(s) inserted. |
| // |
| // The path for such an update is: |
| // - Install the updated breakpoint |
| // - In the operation succeeded |
| // - Remove the old breakpoint(s) |
| // - Perform any pending update |
| |
| // Update completion monitor |
| final CountingRequestMonitor updateRM = new CountingRequestMonitor(getExecutor(), rm) { |
| @Override |
| protected void handleSuccess() { |
| // Success: simply store the new attributes |
| platformBPs.put(breakpoint, attributes); |
| rm.done(); |
| } |
| |
| @Override |
| protected void handleError() { |
| // Store the error message to use in the problem marker |
| final String errorMessage = getStatus().getException() != null |
| ? getStatus().getException().getLocalizedMessage() |
| : getStatus().getMessage(); |
| // Try to uninstall the target breakpoints and add the problem marker |
| // with the error message to the platform breakpoint. |
| doUninstallBreakpoint(dmc, breakpoint, new ImmediateRequestMonitor(rm) { |
| @Override |
| protected void handleSuccess() { |
| addBreakpointProblemMarker(breakpoint, errorMessage, IMarker.SEVERITY_WARNING); |
| rm.done(); |
| } |
| |
| @Override |
| protected void handleError() { |
| // Reset the breakpoint attributes. This will trigger a |
| // breakpoint change event and the correct delta will be |
| // computed, resulting in a correctly restored breakpoint |
| // at the back-end. |
| rollbackAttributes(breakpoint, oldValues); |
| platformBPs.put(breakpoint, attributes); |
| |
| rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, INVALID_PARAMETER, |
| getStatus().getException())); |
| rm.done(); |
| } |
| }); |
| } |
| }; |
| |
| // Everything OK: remove the old back-end breakpoints |
| final Vector<IBreakpointDMContext> newTargetBPs = new Vector<>(); |
| final CountingRequestMonitor removeRM = new CountingRequestMonitor(getExecutor(), rm) { |
| @Override |
| protected void handleSuccess() { |
| // All right! Save the new list and perform the final update |
| Map<ICBreakpoint, Vector<IBreakpointDMContext>> breakpointIDs = fPlatformToBPsMaps.get(dmc); |
| if (breakpointIDs == null) { |
| rm.setStatus( |
| new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, INVALID_BREAKPOINT, null)); |
| rm.done(); |
| return; |
| } |
| breakpointIDs.put(breakpoint, newTargetBPs); |
| for (IBreakpointDMContext ref : newTargetBPs) { |
| fBreakpoints.updateBreakpoint(ref, attributesDelta, updateRM); |
| } |
| updateRM.setDoneCount(newTargetBPs.size()); |
| } |
| }; |
| |
| // New back-end breakpoints insertion monitor |
| // Holds the list of new back-end breakpoint contexts of the platform breakpoint |
| final DataRequestMonitor<Vector<IBreakpointDMContext>> insertRM = new DataRequestMonitor<Vector<IBreakpointDMContext>>( |
| getExecutor(), null) { |
| |
| @Override |
| // In theory, we could have had a partial success and the original threads |
| // list would be invalid. We think it is highly unlikely so we assume that |
| // either everything went fine or else everything failed. |
| protected void handleSuccess() { |
| // Get the list of new back-end breakpoints contexts |
| newTargetBPs.addAll(getData()); |
| for (IBreakpointDMContext newRef : newTargetBPs) { |
| targetBPs.put(newRef, breakpoint); |
| } |
| threadsIDs.put(breakpoint, newThreads); |
| for (final IBreakpointDMContext ref : oldTargetBPs) { |
| targetBPs.remove(ref); |
| decrementInstallCount(ref, breakpoint, // A tad early but it should work... |
| new RequestMonitor(getExecutor(), removeRM) { |
| @Override |
| protected void handleCompleted() { |
| fBreakpoints.removeBreakpoint(ref, removeRM); |
| } |
| }); |
| } |
| removeRM.setDoneCount(oldTargetBPs.size()); |
| } |
| |
| @Override |
| protected void handleError() { |
| // Store the error message to use in the problem marker |
| final String errorMessage = getStatus().getException() != null |
| ? getStatus().getException().getLocalizedMessage() |
| : getStatus().getMessage(); |
| doUninstallBreakpoint(dmc, breakpoint, new ImmediateRequestMonitor(updateRM) { |
| @Override |
| protected void handleSuccess() { |
| addBreakpointProblemMarker(breakpoint, errorMessage, IMarker.SEVERITY_WARNING); |
| updateRM.setDoneCount(0); |
| } |
| }); |
| } |
| }; |
| |
| // If the changes in the breakpoint attributes justify it, install a |
| // new set of back-end breakpoint(s) and then update them |
| if (needsResinstallation(attributesDelta)) { |
| reinstallBreakpoint(dmc, breakpoint, attributes, newThreads, insertRM); |
| } else { |
| // Update the back-end breakpoint(s) state |
| for (IBreakpointDMContext ref : oldTargetBPs) { |
| fBreakpoints.updateBreakpoint(ref, attributesDelta, updateRM); |
| } |
| updateRM.setDoneCount(oldTargetBPs.size()); |
| } |
| } |
| |
| /** |
| * Re-install the back-end breakpoints |
| * |
| * @param context the target context |
| * @param breakpoint the platform breakpoint |
| * @param attributes breakpoint augmented attributes |
| * @param threads list of threads where breakpoint is to be installed |
| * @param drm will contain the list of successfully installed back-end breakpoints |
| */ |
| private void reinstallBreakpoint(final IBreakpointsTargetDMContext context, final ICBreakpoint breakpoint, |
| final Map<String, Object> attributes, Set<String> threads, |
| final DataRequestMonitor<Vector<IBreakpointDMContext>> drm) { |
| // Our new list of back-end breakpoints. Built as we go. |
| final Vector<IBreakpointDMContext> breakpointList = new Vector<>(); |
| |
| // Counting monitor for the new back-end breakpoints to install |
| // Once we're done, return the new list of back-end breakpoints contexts |
| final CountingRequestMonitor installRM = new CountingRequestMonitor(getExecutor(), drm) { |
| @Override |
| protected void handleSuccess() { |
| // Report whatever we have managed to install |
| // It is very likely installation either succeeded or failed for all |
| drm.setData(breakpointList); |
| drm.done(); |
| } |
| }; |
| installRM.setDoneCount(threads.size()); |
| |
| // And install the new back-end breakpoints |
| for (String thread : threads) { |
| // Convert the breakpoint attributes for the back-end |
| // Refresh the set of attributes at each iteration just in case... |
| Map<String, Object> attrs = convertToTargetBreakpoint(breakpoint, attributes); |
| // Tracepoints and dynamic printf are not affected by "skip-all" |
| if (!(breakpoint instanceof ICTracepoint) && !(breakpoint instanceof ICDynamicPrintf) |
| && !fBreakpointManager.isEnabled()) { |
| attrs.put(MIBreakpoints.IS_ENABLED, false); |
| } |
| |
| attrs.put(MIBreakpointDMData.THREAD_ID, thread); |
| |
| // Then install the spiked breakpoint |
| fBreakpoints.insertBreakpoint(context, attrs, |
| new DataRequestMonitor<IBreakpointDMContext>(getExecutor(), installRM) { |
| @Override |
| protected void handleSuccess() { |
| // Add the new back-end breakpoint context to the list |
| breakpointList.add(getData()); |
| |
| // Check for a pending breakpoint before showing that it was properly installed |
| fBreakpoints.getBreakpointDMData(getData(), |
| new DataRequestMonitor<IBreakpointDMData>(getExecutor(), null) { |
| @Override |
| protected void handleCompleted() { |
| boolean pending = false; |
| if (isSuccess()) { |
| IBreakpointDMData data = getData(); |
| if (data instanceof MIBreakpointDMData) { |
| pending = ((MIBreakpointDMData) data).isPending(); |
| } |
| } |
| // Finally, update the platform breakpoint to show it was installed, unless we have a pending breakpoint |
| if (!pending) { |
| try { |
| breakpoint.incrementInstallCount(); |
| } catch (CoreException e) { |
| } |
| } |
| installRM.done(); |
| } |
| }); |
| } |
| |
| @Override |
| protected void handleError() { |
| String detailedMessage; |
| if (getStatus().getException() != null && getStatus().getException().getMessage() != null) { |
| detailedMessage = getStatus().getException().getMessage(); |
| } else { |
| detailedMessage = getStatus().getMessage(); |
| } |
| String description = (detailedMessage == null) ? Messages.Breakpoint_attribute_problem |
| : MessageFormat.format(Messages.Breakpoint_attribute_detailed_problem, |
| new Object[] { detailedMessage }); |
| // Add the new back-end breakpoint context to the list |
| installRM.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, |
| description, getStatus().getException())); |
| installRM.done(); |
| } |
| }); |
| } |
| } |
| |
| /////////////////////////////////////////////////////////////////////////// |
| // IBreakpointManagerListener implementation |
| /////////////////////////////////////////////////////////////////////////// |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.debug.core.IBreakpointManagerListener#breakpointManagerEnablementChanged(boolean) |
| */ |
| @Override |
| public void breakpointManagerEnablementChanged(boolean enabled) { |
| |
| // Only modify enabled breakpoints |
| for (IBreakpointsTargetDMContext context : fPlatformToBPsMaps.keySet()) { |
| for (ICBreakpoint breakpoint : fPlatformToBPsMaps.get(context).keySet()) { |
| try { |
| // Note that Tracepoints and dynamic printf are not affected by "skip-all" |
| if (!(breakpoint instanceof ICTracepoint) && !(breakpoint instanceof ICDynamicPrintf) |
| && breakpoint.isEnabled()) { |
| for (IBreakpointDMContext ref : fPlatformToBPsMaps.get(context).get(breakpoint)) { |
| Map<String, Object> delta = new HashMap<>(); |
| delta.put(MIBreakpoints.IS_ENABLED, enabled); |
| fBreakpoints.updateBreakpoint(ref, delta, new RequestMonitor(getExecutor(), null)); |
| } |
| } |
| } catch (CoreException e) { |
| } |
| } |
| } |
| } |
| |
| /////////////////////////////////////////////////////////////////////////// |
| // IBreakpointListener implementation |
| /////////////////////////////////////////////////////////////////////////// |
| |
| @ThreadSafe |
| @Override |
| public void breakpointAdded(final IBreakpoint breakpoint) { |
| breakpointAdded(breakpoint, null, new RequestMonitor(getExecutor(), null)); |
| } |
| |
| /** |
| * Extension of {@link #breakpointAdded(IBreakpoint)} |
| * |
| * @param miBpt |
| * the MIBreakpoint that initiated the breakpointAdded, or null |
| * @since 5.3 |
| * @deprecated Use |
| * {@link #breakpointAdded(IBreakpoint, MIBreakpoint, RequestMonitor)} |
| * instead. See Bug 530377. |
| */ |
| @ThreadSafe |
| @Deprecated |
| public void breakpointAdded(final IBreakpoint breakpoint, MIBreakpoint miBpt) { |
| breakpointAdded(breakpoint, miBpt, new RequestMonitor(getExecutor(), null)); |
| } |
| |
| /** |
| * Extension of {@link #breakpointAdded(IBreakpoint)} that can be monitored for |
| * completeness with a {@link RequestMonitor}. |
| * |
| * @param breakpoint |
| * the added breakpoint |
| * @param miBpt |
| * the MIBreakpoint that initiated the breakpointAdded, or null |
| * @param rm |
| * @since 5.5 |
| */ |
| @ThreadSafe |
| public void breakpointAdded(final IBreakpoint breakpoint, MIBreakpoint miBpt, RequestMonitor rm) { |
| if (supportsBreakpoint(breakpoint)) { |
| try { |
| // Retrieve the breakpoint attributes |
| final Map<String, Object> attrs = breakpoint.getMarker().getAttributes(); |
| |
| getExecutor().execute(new DsfRunnable() { |
| @Override |
| public void run() { |
| // For a new breakpoint, the first thing we do is set the target filter to all existing processes. |
| // We will need this when it is time to install the breakpoint. |
| // We fetch the processes from our IProcess service to be generic (bug 431986) |
| fProcesses.getProcessesBeingDebugged(fConnection.getContext(), |
| new DataRequestMonitor<IDMContext[]>(getExecutor(), rm) { |
| @Override |
| protected void handleCompleted() { |
| if (isSuccess()) { |
| try { |
| IDsfBreakpointExtension filterExtension = getFilterExtension( |
| (ICBreakpoint) breakpoint); |
| for (IDMContext dmc : getData()) { |
| IContainerDMContext containerDmc = DMContexts.getAncestorOfType(dmc, |
| IContainerDMContext.class); |
| assert containerDmc != null; |
| if (filterExtension.getThreadFilters(containerDmc) == null) { |
| // Do this only if there wasn't already an entry, or else we would |
| // erase the content of that previous entry. |
| // There can be an entry already when a thread-specific breakpoint is created |
| // from the MIBreakpointsSynchronizer (through the gdb console). In that case the |
| // platform bp gets created, and the targetFilter gets set by MIBreakpointsSynchronizer |
| // before the call to breakpointAdded() is made and we get to here. |
| // Bug 433329 |
| filterExtension.setTargetFilter(containerDmc); |
| } |
| } |
| } catch (CoreException e1) { |
| // Error setting target filter, just skip altogether |
| } |
| } |
| |
| // Now we can install the bp for all target contexts |
| final CountingRequestMonitor countingRm = new CountingRequestMonitor( |
| getExecutor(), rm) { |
| @Override |
| protected void handleCompleted() { |
| // Log any error when creating the breakpoint |
| if (getStatus().getSeverity() == IStatus.ERROR) { |
| GdbPlugin.getDefault().getLog().log(getStatus()); |
| } |
| rm.done(); |
| } |
| }; |
| countingRm.setDoneCount(getTrackedBreakpointTargetContexts().size()); |
| |
| for (final IBreakpointsTargetDMContext dmc : getTrackedBreakpointTargetContexts()) { |
| boolean filtered = isBreakpointEntirelyFiltered(dmc, |
| (ICBreakpoint) breakpoint); |
| if (!filtered) { |
| determineDebuggerPath(dmc, attrs, |
| new RequestMonitor(getExecutor(), countingRm) { |
| @Override |
| protected void handleSuccess() { |
| installBreakpoint(dmc, (ICBreakpoint) breakpoint, attrs, |
| countingRm); |
| } |
| }); |
| } else { |
| countingRm.done(); |
| } |
| } |
| } |
| }); |
| } |
| }); |
| |
| // Normal return case, rm handling passed to runnable |
| return; |
| } catch (CoreException e) { |
| } catch (RejectedExecutionException e) { |
| } |
| } |
| |
| // error/abnormal return case |
| rm.done(); |
| } |
| |
| /** |
| * @param bp |
| * @return |
| * @throws CoreException |
| * @since 4.7 |
| */ |
| protected IDsfBreakpointExtension getFilterExtension(ICBreakpoint bp) throws CoreException { |
| return (IDsfBreakpointExtension) bp.getExtension(GDB_DEBUG_MODEL_ID, ICBreakpointExtension.class); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.debug.core.IBreakpointListener#breakpointChanged(org.eclipse.debug.core.model.IBreakpoint, org.eclipse.core.resources.IMarkerDelta) |
| */ |
| @Override |
| public void breakpointChanged(final IBreakpoint breakpoint, final IMarkerDelta delta) { |
| |
| if (supportsBreakpoint(breakpoint)) { |
| |
| try { |
| // Retrieve the breakpoint attributes |
| final Map<String, Object> attrs = breakpoint.getMarker().getAttributes(); |
| // Tracepoints and dynamic printf are not affected by "skip-all" |
| if (!(breakpoint instanceof ICTracepoint) && !(breakpoint instanceof ICDynamicPrintf) |
| && !fBreakpointManager.isEnabled()) { |
| attrs.put(ICBreakpoint.ENABLED, false); |
| } |
| |
| // Modify the breakpoint in all the target contexts |
| getExecutor().execute(new DsfRunnable() { |
| @Override |
| public void run() { |
| |
| // If the breakpoint is currently being updated, queue the request and exit |
| if (fPendingRequests.contains(breakpoint)) { |
| fPendingBreakpoints.add(breakpoint); |
| return; |
| } |
| |
| // Keep track of the updates |
| final CountingRequestMonitor countingRm = new CountingRequestMonitor(getExecutor(), null) { |
| @Override |
| protected void handleCompleted() { |
| |
| if (!isSuccess() && getStatus().getSeverity() == IStatus.ERROR) { |
| GdbPlugin.getDefault().getLog().log(getStatus()); |
| } |
| |
| // Indicate that the pending request has completed |
| fPendingRequests.remove(breakpoint); |
| |
| // Process the next pending update for this breakpoint |
| if (fPendingBreakpoints.contains(breakpoint)) { |
| fPendingBreakpoints.remove(breakpoint); |
| breakpointChanged(breakpoint, delta); |
| } |
| } |
| }; |
| countingRm.setDoneCount(getTrackedBreakpointTargetContexts().size()); |
| |
| // Mark the breakpoint as being updated and go |
| fPendingRequests.add(breakpoint); |
| |
| // Modify the breakpoint in all the execution contexts |
| for (final IBreakpointsTargetDMContext dmc : getTrackedBreakpointTargetContexts()) { |
| determineDebuggerPath(dmc, attrs, new RequestMonitor(getExecutor(), countingRm) { |
| @Override |
| protected void handleSuccess() { |
| modifyBreakpoint(dmc, (ICBreakpoint) breakpoint, attrs, delta, countingRm); |
| } |
| }); |
| } |
| } |
| }); |
| |
| } catch (CoreException e) { |
| } catch (RejectedExecutionException e) { |
| } |
| } |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.debug.core.IBreakpointListener#breakpointRemoved(org.eclipse.debug.core.model.IBreakpoint, org.eclipse.core.resources.IMarkerDelta) |
| */ |
| @Override |
| public void breakpointRemoved(final IBreakpoint breakpoint, IMarkerDelta delta) { |
| |
| if (supportsBreakpoint(breakpoint)) { |
| try { |
| getExecutor().execute(new DsfRunnable() { |
| @Override |
| public void run() { |
| CountingRequestMonitor countingRm = new CountingRequestMonitor(getExecutor(), null) { |
| @Override |
| protected void handleError() { |
| if (getStatus().getSeverity() == IStatus.ERROR) { |
| GdbPlugin.getDefault().getLog().log(getStatus()); |
| } |
| } |
| }; |
| countingRm.setDoneCount(getTrackedBreakpointTargetContexts().size()); |
| |
| // Remove the breakpoint in all the execution contexts |
| for (IBreakpointsTargetDMContext dmc : getTrackedBreakpointTargetContexts()) { |
| if (fPlatformToAttributesMaps.get(dmc).containsKey(breakpoint)) { |
| uninstallBreakpoint(dmc, (ICBreakpoint) breakpoint, countingRm); |
| } |
| } |
| } |
| }); |
| } catch (RejectedExecutionException e) { |
| } |
| } |
| } |
| |
| /////////////////////////////////////////////////////////////////////////// |
| // IServiceEventListener |
| /////////////////////////////////////////////////////////////////////////// |
| |
| //------------------------------------------------------------------------- |
| // Breakpoints |
| //------------------------------------------------------------------------- |
| |
| @DsfServiceEventHandler |
| public void eventDispatched(BreakpointAddedEvent e) { |
| // Nothing to do - already handled by breakpointAdded() |
| } |
| |
| @DsfServiceEventHandler |
| public void eventDispatched(BreakpointUpdatedEvent e) { |
| // Nothing to do - already handled by breakpointChanged() |
| } |
| |
| @DsfServiceEventHandler |
| public void eventDispatched(BreakpointRemovedEvent e) { |
| // Nothing to do - already handled by breakpointRemoved() |
| } |
| |
| /* |
| * When a watchpoint goes out of scope, it is automatically removed from |
| * the back-end. To keep our internal state synchronized, we have to |
| * remove it from our breakpoints maps. |
| * Unfortunately, GDB doesn't generate the correct event... |
| */ |
| @DsfServiceEventHandler |
| public void eventDispatched(MIWatchpointScopeEvent e) { |
| } |
| |
| //------------------------------------------------------------------------- |
| // Breakpoint actions |
| //------------------------------------------------------------------------- |
| |
| /** @since 4.2 */ |
| @DsfServiceEventHandler |
| public void eventDispatched(ISuspendedDMEvent e) { |
| assert e instanceof IMIDMEvent; |
| if (e instanceof IMIDMEvent) { |
| Object miEvent = ((IMIDMEvent) e).getMIEvent(); |
| |
| if (miEvent instanceof MIBreakpointHitEvent) { |
| // This covers catchpoints, too |
| MIBreakpointHitEvent evt = (MIBreakpointHitEvent) miEvent; |
| performBreakpointAction(evt.getDMContext(), evt.getNumber()); |
| return; |
| } |
| |
| if (miEvent instanceof MIWatchpointTriggerEvent) { |
| MIWatchpointTriggerEvent evt = (MIWatchpointTriggerEvent) miEvent; |
| performBreakpointAction(evt.getDMContext(), evt.getNumber()); |
| return; |
| } |
| } |
| } |
| |
| /** |
| * @deprecated Replaced by the generic {@link #eventDispatched(ISuspendedDMEvent)} |
| */ |
| @Deprecated |
| @DsfServiceEventHandler |
| public void eventDispatched(SuspendedEvent e) { |
| } |
| |
| private void performBreakpointAction(final IDMContext context, String number) { |
| // Identify the platform breakpoint |
| final ICBreakpoint breakpoint = findPlatformBreakpoint(number); |
| |
| if (breakpoint != null) { |
| // Perform the actions asynchronously (otherwise we can have a deadlock...) |
| new Job("Breakpoint action") { //$NON-NLS-1$ |
| { |
| setSystem(true); |
| } |
| |
| @Override |
| protected IStatus run(IProgressMonitor monitor) { |
| fBreakpointActionManager.executeActions(breakpoint, |
| new BreakpointActionAdapter(getExecutor(), getServicesTracker(), context)); |
| return Status.OK_STATUS; |
| } |
| }.schedule(); |
| } |
| } |
| |
| // Helper function to locate the platform breakpoint corresponding |
| // to the target breakpoint/watchpoint that was just hit |
| |
| // FIXME: (Bug228703) Need a way to identify the correct context where the BP was hit |
| private ICBreakpoint findPlatformBreakpoint(String targetBreakpointID) { |
| Set<IBreakpointsTargetDMContext> targets = fBPToPlatformMaps.keySet(); |
| for (IBreakpointsTargetDMContext target : targets) { |
| Map<IBreakpointDMContext, ICBreakpoint> bps = fBPToPlatformMaps.get(target); |
| Set<IBreakpointDMContext> contexts = bps.keySet(); |
| for (IBreakpointDMContext context : contexts) { |
| if (context instanceof MIBreakpointDMContext) { |
| MIBreakpointDMContext ctx = (MIBreakpointDMContext) context; |
| if (ctx.getReference().equals(targetBreakpointID)) { |
| return bps.get(context); |
| } |
| } |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Returns a platform breakpoint corresponding to a given target breakpoint. |
| * |
| * @since 3.0 |
| */ |
| public IBreakpoint findPlatformBreakpoint(IBreakpointDMContext bpContext) { |
| if (bpContext instanceof MIBreakpointDMContext) { |
| IBreakpointsTargetDMContext targetCtx = DMContexts.getAncestorOfType(bpContext, |
| IBreakpointsTargetDMContext.class); |
| if (targetCtx != null) { |
| Map<IBreakpointDMContext, ICBreakpoint> bps = fBPToPlatformMaps.get(targetCtx); |
| if (bps != null) { |
| return bps.get(bpContext); |
| } |
| } |
| } |
| return null; |
| } |
| |
| //------------------------------------------------------------------------- |
| // Process/thread start/exit |
| //------------------------------------------------------------------------- |
| /** |
| * @noreference This method is not intended to be referenced by clients. |
| * @since 4.4 |
| */ |
| @DsfServiceEventHandler |
| public void eventDispatched(IStartedDMEvent e) { |
| } |
| |
| private void setTargetFilter(ICBreakpoint breakpoint, IContainerDMContext containerDmc) { |
| try { |
| IDsfBreakpointExtension filterExt = getFilterExtension(breakpoint); |
| if (filterExt.getThreadFilters(containerDmc) == null) { |
| // Do this only if there wasn't already an entry, or else we would |
| // erase the content of that previous entry. |
| // This could theoretically happen if the targetFilter is set by |
| // someone else, before this method is called. |
| // Bug 433329 |
| filterExt.setTargetFilter(containerDmc); |
| } |
| |
| } catch (CoreException e) { |
| } |
| } |
| |
| /** |
| * @noreference This method is not intended to be referenced by clients. |
| * @since 4.4 |
| */ |
| @DsfServiceEventHandler |
| public void eventDispatched(IExitedDMEvent e) { |
| // original code moved to API removeTargetFilter (Bug 456959) |
| } |
| |
| /** |
| * Remove process from the thread filtering of all breakpoints |
| * @since 4.6 |
| */ |
| public void removeTargetFilter(IContainerDMContext containerDMC) { |
| // We must get the list of breakpoints from the platform because our different |
| // maps might already have been cleaned up |
| IBreakpoint[] allBreakpoints = fBreakpointManager.getBreakpoints(fDebugModelId); |
| for (IBreakpoint bp : allBreakpoints) { |
| if (supportsBreakpoint(bp)) { |
| removeTargetFilter((ICBreakpoint) bp, containerDMC); |
| } |
| } |
| } |
| |
| private void removeTargetFilter(ICBreakpoint breakpoint, IContainerDMContext containerDmc) { |
| try { |
| IDsfBreakpointExtension filterExt = getFilterExtension(breakpoint); |
| filterExt.removeTargetFilter(containerDmc); |
| } catch (CoreException e) { |
| } |
| } |
| |
| private void removeAllTargetFilters(IBreakpointsTargetDMContext bpTargetDmc, ICBreakpoint breakpoint) { |
| try { |
| IDsfBreakpointExtension filterExt = getFilterExtension(breakpoint); |
| IContainerDMContext[] targets = filterExt.getTargetFilters(); |
| for (IContainerDMContext target : targets) { |
| if (bpTargetDmc.equals(target) || DMContexts.isAncestorOf(target, bpTargetDmc)) { |
| filterExt.removeTargetFilter(target); |
| } |
| } |
| } catch (CoreException e) { |
| } |
| } |
| |
| //------------------------------------------------------------------------- |
| // Session exit |
| //------------------------------------------------------------------------- |
| |
| /** |
| * @since 1.1 |
| * @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. |
| */ |
| @DsfServiceEventHandler |
| public void eventDispatched(ICommandControlShutdownDMEvent e) { |
| terminated(); |
| } |
| |
| private void terminated() { |
| // Reset the breakpoint install count |
| for (IBreakpointsTargetDMContext ctx : fPlatformToAttributesMaps.keySet()) { |
| Map<ICBreakpoint, Map<String, Object>> breakpoints = fPlatformToAttributesMaps.get(ctx); |
| clearBreakpointStatus(breakpoints.keySet().toArray(new ICBreakpoint[breakpoints.size()]), ctx); |
| } |
| // This will prevent Shutdown() from trying to remove bps from a |
| // backend that has already shutdown |
| fPlatformToAttributesMaps.clear(); |
| } |
| |
| /** |
| * @param bps |
| */ |
| private void clearBreakpointStatus(final ICBreakpoint[] bps, final IBreakpointsTargetDMContext ctx) { |
| IWorkspaceRunnable wr = monitor -> { |
| // For every platform breakpoint that has at least one target breakpoint installed |
| // we must decrement the install count, for every target breakpoint. |
| // Note that we cannot simply call resetInstallCount() because another |
| // launch may be using the same platform breakpoint. |
| Map<ICBreakpoint, Vector<IBreakpointDMContext>> breakpoints = fPlatformToBPsMaps.get(ctx); |
| for (ICBreakpoint breakpoint : breakpoints.keySet()) { |
| Vector<IBreakpointDMContext> targetBps = breakpoints.get(breakpoint); |
| for (IBreakpointDMContext targetBp : targetBps) { |
| decrementInstallCount(targetBp, breakpoint, new RequestMonitor(getExecutor(), null)); |
| } |
| } |
| }; |
| |
| // Create the scheduling rule to clear all bp planted. |
| ISchedulingRule rule = null; |
| List<ISchedulingRule> markerRules = new ArrayList<>(); |
| for (ICBreakpoint bp : bps) { |
| IMarker marker = bp.getMarker(); |
| if (marker != null) { |
| ISchedulingRule markerRule = ResourcesPlugin.getWorkspace().getRuleFactory() |
| .markerRule(marker.getResource()); |
| if (markerRule == null) { |
| markerRules = null; |
| break; |
| } else { |
| markerRules.add(markerRule); |
| } |
| } |
| } |
| if (markerRules != null) { |
| rule = MultiRule.combine(markerRules.toArray(new ISchedulingRule[markerRules.size()])); |
| } |
| |
| try { |
| // Will run the workspace runnable on the current thread, which |
| // is the DSF executor. |
| ResourcesPlugin.getWorkspace().run(wr, rule, 0, null); |
| } catch (CoreException e) { |
| GdbPlugin.getDefault().getLog().log(e.getStatus()); |
| } |
| |
| new Job("Clear Breakpoints Status") { //$NON-NLS-1$ |
| @Override |
| protected IStatus run(IProgressMonitor monitor) { |
| // Clear any problem markers |
| for (IMarker marker : fBreakpointMarkerProblems.values()) { |
| if (marker != null) { |
| try { |
| marker.delete(); |
| } catch (CoreException e) { |
| } |
| } |
| } |
| fBreakpointMarkerProblems.clear(); |
| |
| return Status.OK_STATUS; |
| } |
| }.schedule(); |
| } |
| |
| /////////////////////////////////////////////////////////////////////////// |
| // Support functions |
| /////////////////////////////////////////////////////////////////////////// |
| |
| /** |
| * Indicates if the platform breakpoint object [bp] is one we can deal with. |
| * For now, it boils down to whether it's a CDT Breakpoint (an |
| * ICBreakpoint). DSF can supports other (custom) types of breakpoints, but |
| * DSF-GDB is tied to ICBreakpoint. |
| * |
| * @param bp the platform breakpoint |
| * @return true if we support it; false otherwise |
| * @since 4.7 |
| */ |
| protected boolean supportsBreakpoint(IBreakpoint bp) { |
| if (bp instanceof ICBreakpoint && bp.getModelIdentifier().equals(fDebugModelId)) { |
| IMarker marker = bp.getMarker(); |
| if (marker != null) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * determineDebuggerPath |
| * |
| * Adds the path to the source file to the set of attributes |
| * (for the debugger). |
| * |
| * @param dmc |
| * @param attributes |
| * @param rm |
| */ |
| private void determineDebuggerPath(IBreakpointsTargetDMContext dmc, final Map<String, Object> attributes, |
| final RequestMonitor rm) { |
| String hostPath = (String) attributes.get(ICBreakpoint.SOURCE_HANDLE); |
| |
| if (hostPath != null) { |
| |
| ISourceLookupDMContext srcDmc = DMContexts.getAncestorOfType(dmc, ISourceLookupDMContext.class); |
| if (srcDmc != null) { |
| fSourceLookup.getDebuggerPath(srcDmc, hostPath, new DataRequestMonitor<String>(getExecutor(), rm) { |
| @Override |
| protected void handleSuccess() { |
| attributes.put(ATTR_DEBUGGER_PATH, adjustDebuggerPath(getData())); |
| rm.done(); |
| } |
| }); |
| } else { |
| // Source lookup not available for given context, use the host |
| // path for the debugger path. |
| attributes.put(ATTR_DEBUGGER_PATH, adjustDebuggerPath(hostPath)); |
| rm.done(); |
| } |
| } else { |
| // Some types of breakpoints do not require a path |
| // (e.g. watchpoints) |
| rm.done(); |
| } |
| } |
| |
| /** |
| * For some platforms (MinGW) the debugger path needs to be adjusted to work |
| * with earlier GDB versions. |
| * See https://bugs.eclipse.org/bugs/show_bug.cgi?id=232415 |
| * |
| * @param path |
| * the absolute path to the source file |
| * @return the adjusted path provided by the breakpoints service. |
| */ |
| String adjustDebuggerPath(String path) { |
| return (fBreakpoints instanceof IMIBreakpointPathAdjuster) |
| ? ((IMIBreakpointPathAdjuster) fBreakpoints).adjustDebuggerPath(path) |
| : path; |
| } |
| |
| /** |
| * Determine the set of modified attributes. |
| * Elementary set operations in full action :-) |
| * |
| * @param oldAttributes |
| * @param newAttributes |
| * @return |
| */ |
| private Map<String, Object> determineAttributesDelta(Map<String, Object> oldAttributes, |
| Map<String, Object> newAttributes) { |
| |
| Map<String, Object> delta = new HashMap<>(); |
| |
| Set<String> oldKeySet = oldAttributes.keySet(); |
| Set<String> newKeySet = newAttributes.keySet(); |
| |
| Set<String> commonKeys = new HashSet<>(newKeySet); |
| commonKeys.retainAll(oldKeySet); |
| Set<String> addedKeys = new HashSet<>(newKeySet); |
| addedKeys.removeAll(oldKeySet); |
| Set<String> removedKeys = new HashSet<>(oldKeySet); |
| removedKeys.removeAll(newKeySet); |
| |
| // Add the modified attributes |
| for (String key : commonKeys) { |
| if (!(oldAttributes.get(key).equals(newAttributes.get(key)))) { |
| delta.put(key, newAttributes.get(key)); |
| } |
| } |
| |
| // Add the new attributes |
| for (String key : addedKeys) { |
| delta.put(key, newAttributes.get(key)); |
| } |
| |
| // Remove the deleted attributes |
| for (String key : removedKeys) { |
| delta.put(key, null); |
| } |
| |
| return convertToPlatformAttributes(delta); |
| } |
| |
| /** |
| * Converts ICBreakpoint attributes to IBreakpoints attributes. |
| * |
| * @param cdtAttributes |
| * @return |
| * @since 5.3 |
| */ |
| protected Map<String, Object> convertToPlatformAttributes(Map<String, Object> cdtAttributes) { |
| |
| Map<String, Object> result = new HashMap<>(); |
| |
| // IBreakpoint attributes |
| if (cdtAttributes.containsKey(ATTR_DEBUGGER_PATH)) { |
| result.put(MIBreakpoints.FILE_NAME, cdtAttributes.get(ATTR_DEBUGGER_PATH)); |
| } |
| |
| if (cdtAttributes.containsKey(IMarker.LINE_NUMBER)) { |
| result.put(MIBreakpoints.LINE_NUMBER, cdtAttributes.get(IMarker.LINE_NUMBER)); |
| } |
| |
| if (cdtAttributes.containsKey(BreakpointActionManager.BREAKPOINT_ACTION_ATTRIBUTE)) { |
| result.put(MIBreakpoints.COMMANDS, cdtAttributes.get(BreakpointActionManager.BREAKPOINT_ACTION_ATTRIBUTE)); |
| } |
| |
| // ICLineBreakpoint attributes |
| if (cdtAttributes.containsKey(ICLineBreakpoint.FUNCTION)) { |
| result.put(MIBreakpoints.FUNCTION, cdtAttributes.get(ICLineBreakpoint.FUNCTION)); |
| } |
| |
| if (cdtAttributes.containsKey(ICLineBreakpoint.ADDRESS)) { |
| result.put(MIBreakpoints.ADDRESS, cdtAttributes.get(ICLineBreakpoint.ADDRESS)); |
| } |
| |
| // ICBreakpoint attributes |
| if (cdtAttributes.containsKey(ICBreakpoint.CONDITION)) { |
| result.put(MIBreakpoints.CONDITION, cdtAttributes.get(ICBreakpoint.CONDITION)); |
| } |
| |
| if (cdtAttributes.containsKey(ICBreakpoint.IGNORE_COUNT)) { |
| result.put(MIBreakpoints.IGNORE_COUNT, cdtAttributes.get(ICBreakpoint.IGNORE_COUNT)); |
| } |
| |
| if (cdtAttributes.containsKey(ICTracepoint.PASS_COUNT)) { |
| result.put(MIBreakpoints.PASS_COUNT, cdtAttributes.get(ICTracepoint.PASS_COUNT)); |
| } |
| |
| if (cdtAttributes.containsKey(ICBreakpoint.ENABLED)) { |
| result.put(MIBreakpoints.IS_ENABLED, cdtAttributes.get(ICBreakpoint.ENABLED)); |
| } |
| |
| if (cdtAttributes.containsKey(ICBreakpointType.TYPE)) { |
| result.put(MIBreakpoints.BREAKPOINT_TYPE, cdtAttributes.get(ICBreakpointType.TYPE)); |
| } |
| |
| // ICWatchpoint attributes |
| if (cdtAttributes.containsKey(ICWatchpoint.EXPRESSION)) { |
| result.put(MIBreakpoints.EXPRESSION, cdtAttributes.get(ICWatchpoint.EXPRESSION)); |
| } |
| |
| if (cdtAttributes.containsKey(ICWatchpoint.READ)) { |
| result.put(MIBreakpoints.READ, cdtAttributes.get(ICWatchpoint.READ)); |
| } |
| |
| if (cdtAttributes.containsKey(ICWatchpoint.WRITE)) { |
| result.put(MIBreakpoints.WRITE, cdtAttributes.get(ICWatchpoint.WRITE)); |
| } |
| |
| // Threads |
| if (cdtAttributes.containsKey(ATTR_THREAD_FILTER)) { |
| result.put(ATTR_THREAD_FILTER, cdtAttributes.get(ATTR_THREAD_FILTER)); |
| } |
| |
| // For IDynamicPrintf |
| if (cdtAttributes.containsKey(ICDynamicPrintf.PRINTF_STRING)) { |
| result.put(MIBreakpoints.PRINTF_STRING, cdtAttributes.get(ICDynamicPrintf.PRINTF_STRING)); |
| } |
| |
| return result; |
| } |
| |
| /** |
| * Figure out the corresponding number of back-end breakpoints |
| * Even though the thread IDs are usually integers, they are |
| * stored as strings in CBreakpoints. |
| * |
| * @param attributes |
| * @return |
| */ |
| @SuppressWarnings("unchecked") |
| private Set<String> getThreads(Map<String, Object> attributes) { |
| Set<String> threads = (Set<String>) attributes.get(ATTR_THREAD_FILTER); |
| if (threads == null) { |
| threads = new HashSet<>(); |
| threads.add("0"); // Thread 0 means all threads //$NON-NLS-1$ |
| } |
| return threads; |
| } |
| |
| /** |
| * Get the list of threads from the breakpoint's thread filtering mechanism |
| * |
| * @param breakpoint |
| * @return |
| */ |
| private Set<String> extractThreads(IBreakpointsTargetDMContext bpTargetDmc, ICBreakpoint breakpoint) { |
| Set<String> results = new HashSet<>(); |
| |
| if (supportsThreads(breakpoint)) { |
| List<IExecutionDMContext[]> threads = new ArrayList<>(1); |
| |
| try { |
| // Retrieve all existing targets. |
| // Note that these targets can be from different debugging sessions since |
| // they are associated with the platform breakpoint. |
| IDsfBreakpointExtension filterExtension = getFilterExtension(breakpoint); |
| IContainerDMContext[] procTargets = filterExtension.getTargetFilters(); |
| |
| // Extract the thread IDs |
| for (IContainerDMContext procDmc : procTargets) { |
| // Look for a target/process that belongs to our session |
| if (procDmc.equals(bpTargetDmc) || DMContexts.isAncestorOf(procDmc, bpTargetDmc)) { |
| IExecutionDMContext[] threadFilters = filterExtension.getThreadFilters(procDmc); |
| if (threadFilters == null) { |
| // The breakpoint applies to the entire process. |
| // For GDB < 7.4, we set the thread to 0 to indicate that the breakpoint |
| // is global for this process. |
| // For GDB >= 7.4, things are more complicated. There will be one bp for all |
| // processes, so by setting the thread to 0, the breakpoint will apply |
| // to all threads of all processes. We don't have a choice as there is no |
| // way to tell GDB to apply to all threads (including any new ones that will |
| // be created) for a single process. |
| // So, in this case, if the bp applies to all threads of one process, it will |
| // automatically apply to all threads of all processes |
| results.add("0"); //$NON-NLS-1$ |
| return results; |
| } else { |
| threads.add(threadFilters); |
| } |
| } |
| } |
| } catch (CoreException e) { |
| // Error with the thread filtering. Default to all threads. |
| results.add("0"); //$NON-NLS-1$ |
| return results; |
| } |
| |
| // If there are no threads to filter on, it means the bp applies to the entire process. |
| if (threads.isEmpty()) { |
| results.add("0"); //$NON-NLS-1$ |
| return results; |
| } |
| |
| for (IExecutionDMContext[] targetThreads : threads) { |
| if (targetThreads != null) { |
| for (IExecutionDMContext thread : targetThreads) { |
| if (thread instanceof IMIExecutionDMContext) { |
| results.add(((IMIExecutionDMContext) thread).getThreadId()); |
| } else { |
| // If any of the threads is not an IMIExecutionDMContext, |
| // we don't support thread filters at all. |
| results.clear(); |
| results.add("0"); //$NON-NLS-1$ |
| return results; |
| } |
| } |
| } else { |
| // Should not happen |
| assert false; |
| } |
| } |
| } else { |
| results.add("0"); //$NON-NLS-1$ |
| } |
| |
| return results; |
| } |
| |
| /////////////////////////////////////////////////////////////////////////// |
| // Non-generic (MI-specific) functions |
| /////////////////////////////////////////////////////////////////////////// |
| |
| /** |
| * Create a collection of DSF-GDB specific breakpoint properties given a |
| * platform/CDT breakpoint object and its properties. Basically, this |
| * determines the set of MI-specific properties to be used in installing the |
| * given breakpoint. |
| * |
| * @param breakpoint |
| * the platform breakpoint object; was created by CDT |
| * @param attributes |
| * the breakpoint's properties. By allowing this to be passed in |
| * (rather than us calling |
| * IBreakpoint#getMarker()#getProperties()), we allow the caller |
| * to specify additional/modified properties. |
| * @return a property bag containing the corresponding DSF-GDB properties |
| */ |
| protected Map<String, Object> convertToTargetBreakpoint(ICBreakpoint breakpoint, Map<String, Object> attributes) { |
| |
| Map<String, Object> properties = new HashMap<>(); |
| |
| if (breakpoint instanceof ICWatchpoint) { |
| properties.put(MIBreakpoints.BREAKPOINT_TYPE, MIBreakpoints.WATCHPOINT); |
| properties.put(MIBreakpoints.EXPRESSION, attributes.get(ICWatchpoint.EXPRESSION)); |
| properties.put(MIBreakpoints.READ, attributes.get(ICWatchpoint.READ)); |
| properties.put(MIBreakpoints.WRITE, attributes.get(ICWatchpoint.WRITE)); |
| properties.put(MIBreakpoints.RANGE, attributes.get(ICWatchpoint2.RANGE)); |
| properties.put(MIBreakpoints.MEMSPACE, attributes.get(ICWatchpoint2.MEMORYSPACE)); |
| } else if (breakpoint instanceof ICLineBreakpoint) { |
| properties.put(MIBreakpoints.BREAKPOINT_TYPE, MIBreakpoints.BREAKPOINT); |
| properties.put(MIBreakpoints.FILE_NAME, attributes.get(ATTR_DEBUGGER_PATH)); |
| properties.put(MIBreakpoints.LINE_NUMBER, attributes.get(IMarker.LINE_NUMBER)); |
| properties.put(MIBreakpoints.FUNCTION, attributes.get(ICLineBreakpoint.FUNCTION)); |
| properties.put(MIBreakpoints.ADDRESS, attributes.get(ICLineBreakpoint.ADDRESS)); |
| properties.put(MIBreakpoints.COMMANDS, attributes.get(BreakpointActionManager.BREAKPOINT_ACTION_ATTRIBUTE)); |
| |
| if (breakpoint instanceof ICTracepoint) { |
| // A tracepoint is a LineBreakpoint, but needs its own type |
| properties.put(MIBreakpoints.BREAKPOINT_TYPE, MIBreakpoints.TRACEPOINT); |
| properties.put(MIBreakpoints.PASS_COUNT, attributes.get(ICTracepoint.PASS_COUNT)); |
| } else if (breakpoint instanceof ICDynamicPrintf) { |
| // A DynamicPrintf is a LineBreakpoint, but needs its own type |
| properties.put(MIBreakpoints.BREAKPOINT_TYPE, MIBreakpoints.DYNAMICPRINTF); |
| properties.put(MIBreakpoints.PRINTF_STRING, attributes.get(ICDynamicPrintf.PRINTF_STRING)); |
| } |
| } else if (breakpoint instanceof ICEventBreakpoint) { |
| properties.put(MIBreakpoints.BREAKPOINT_TYPE, MIBreakpoints.CATCHPOINT); |
| properties.put(MIBreakpoints.CATCHPOINT_TYPE, GdbCatchpoints |
| .eventToGdbCatchpointKeyword((String) attributes.get(ICEventBreakpoint.EVENT_TYPE_ID))); |
| |
| String arg = (String) attributes.get(ICEventBreakpoint.EVENT_ARG); |
| String[] args; |
| if ((arg != null) && (arg.length() != 0)) { |
| args = new String[1]; |
| args[0] = arg; |
| } else { |
| args = new String[0]; |
| } |
| properties.put(MIBreakpoints.CATCHPOINT_ARGS, args); |
| } else { |
| assert false : "platform breakpoint is of an unexpected type: " + breakpoint.getClass().getName(); //$NON-NLS-1$ |
| } |
| |
| // Common fields |
| properties.put(MIBreakpoints.CONDITION, attributes.get(ICBreakpoint.CONDITION)); |
| properties.put(MIBreakpoints.IGNORE_COUNT, attributes.get(ICBreakpoint.IGNORE_COUNT)); |
| properties.put(MIBreakpoints.IS_ENABLED, attributes.get(ICBreakpoint.ENABLED)); |
| properties.put(MIBreakpointDMData.THREAD_ID, attributes.get(ATTR_THREAD_ID)); |
| |
| // checks for the breakpoint type, and adds the hardware/temporary flags |
| Object breakpointType = attributes.get(ICBreakpointType.TYPE); |
| if (breakpointType instanceof Integer) { |
| boolean isHardware = ((Integer) breakpointType & ICBreakpointType.HARDWARE) == ICBreakpointType.HARDWARE; |
| boolean isTemporary = ((Integer) breakpointType & ICBreakpointType.TEMPORARY) == ICBreakpointType.TEMPORARY; |
| properties.put(MIBreakpointDMData.IS_HARDWARE, isHardware); |
| properties.put(MIBreakpointDMData.IS_TEMPORARY, isTemporary); |
| } |
| |
| // Adjust for "skip-all" |
| // Tracepoints and dynamic printf are not affected by "skip-all" |
| if (!(breakpoint instanceof ICTracepoint) && !(breakpoint instanceof ICDynamicPrintf) |
| && !fBreakpointManager.isEnabled()) { |
| properties.put(MIBreakpoints.IS_ENABLED, false); |
| } |
| |
| return properties; |
| } |
| |
| /** |
| * Determine if the modified attributes necessitate |
| * a breakpoint removal/re-installation |
| * |
| * @param delta |
| * @return |
| */ |
| protected boolean needsResinstallation(Map<String, Object> delta) { |
| |
| // Check if there is any modified attribute |
| if (delta == null) { |
| return false; |
| } |
| |
| // Check the "critical" attributes |
| if (delta.containsKey(ATTR_DEBUGGER_PATH) // File name |
| || delta.containsKey(MIBreakpoints.LINE_NUMBER) // Line number |
| || delta.containsKey(MIBreakpoints.BREAKPOINT_TYPE) // breakpoint type |
| || delta.containsKey(MIBreakpoints.FUNCTION) // Function name |
| || delta.containsKey(MIBreakpoints.ADDRESS) // Absolute address |
| || delta.containsKey(ATTR_THREAD_FILTER) // Thread ID |
| || delta.containsKey(MIBreakpoints.EXPRESSION) // Watchpoint expression |
| || delta.containsKey(MIBreakpoints.READ) // Watchpoint type |
| || delta.containsKey(MIBreakpoints.WRITE) // Watchpoint type |
| || delta.containsKey(MIBreakpoints.PRINTF_STRING)) {// Dprintf string |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /** |
| * @param breakpoint |
| * @param oldValues |
| */ |
| protected void rollbackAttributes(ICBreakpoint breakpoint, IMarkerDelta oldValues) { |
| |
| try { |
| String newCondition = breakpoint.getCondition(); |
| if (newCondition == null) { |
| newCondition = NULL_STRING; |
| } |
| String oldCondition = (oldValues != null) ? oldValues.getAttribute(ICBreakpoint.CONDITION, NULL_STRING) |
| : NULL_STRING; |
| if (!oldCondition.equals(newCondition)) { |
| breakpoint.setCondition(oldCondition); |
| } else { |
| breakpoint.setCondition(NULL_STRING); |
| } |
| } catch (CoreException e) { |
| } |
| } |
| |
| /** |
| * Indicates if the back-end supports multiple threads for |
| * this type of breakpoint |
| * |
| * @param breakpoint |
| */ |
| protected boolean supportsThreads(ICBreakpoint breakpoint) { |
| |
| return !(breakpoint instanceof ICWatchpoint); |
| } |
| |
| /** |
| * Returns whether the breakpoint is filtered for the given target. |
| */ |
| private boolean isBreakpointEntirelyFiltered(IBreakpointsTargetDMContext bpTargetDmc, ICBreakpoint breakpoint) { |
| try { |
| IContainerDMContext[] procTargets = getFilterExtension(breakpoint).getTargetFilters(); |
| for (IContainerDMContext procDmc : procTargets) { |
| if (procDmc.equals(bpTargetDmc) || DMContexts.isAncestorOf(procDmc, bpTargetDmc)) { |
| return false; |
| } |
| } |
| } catch (CoreException e) { |
| } |
| return true; |
| } |
| |
| /** |
| * @since 4.2 |
| */ |
| public void addBreakpointsTrackingListener(IMIBreakpointsTrackingListener listener) { |
| fTrackingListeners.add(listener); |
| } |
| |
| /** |
| * @since 4.2 |
| */ |
| public void removeBreakpointsTrackingListener(IMIBreakpointsTrackingListener listener) { |
| fTrackingListeners.remove(listener); |
| } |
| |
| private String[] compareAttributes(Map<String, Object> oldAttr, Map<String, Object> newAttr, String[] exclude) { |
| List<String> list = new ArrayList<>(); |
| Set<String> names = new HashSet<>(oldAttr.keySet()); |
| names.addAll(newAttr.keySet()); |
| for (String n : names) { |
| if (exclude != null && Arrays.asList(exclude).contains(n)) { |
| continue; |
| } |
| Object oldValue = oldAttr.get(n); |
| if (oldValue != null && !oldValue.equals(newAttr.get(n))) { |
| list.add(n); |
| } else if (oldValue == null) { |
| if (newAttr.get(n) != null) { |
| list.add(n); |
| } |
| } |
| } |
| return list.toArray(new String[list.size()]); |
| } |
| } |