From 3c3cbab6e717ee65e63ea20970b562c386fe4c05 Mon Sep 17 00:00:00 2001 From: Pawel Piech Date: Fri, 8 Jan 2010 18:45:28 +0000 Subject: [PATCH] Bug 292468 - [breakpoints] Cannot update breakpoint status properly with current IBreakpointAttributeTranslator --- .../debug/service/BreakpointsMediator.java | 14 + .../debug/service/BreakpointsMediator2.java | 1140 +++++++++++++++++ .../IBreakpointAttributeTranslator.java | 51 +- .../IBreakpointAttributeTranslator2.java | 154 +++ 4 files changed, 1350 insertions(+), 9 deletions(-) create mode 100644 dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/BreakpointsMediator2.java create mode 100644 dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IBreakpointAttributeTranslator2.java diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/BreakpointsMediator.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/BreakpointsMediator.java index 864c3400f20..ec9711d0eb8 100644 --- a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/BreakpointsMediator.java +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/BreakpointsMediator.java @@ -48,7 +48,21 @@ import org.eclipse.debug.core.model.IBreakpoint; import org.osgi.framework.BundleContext; /** + * Breakpoints mediator is a DSF service which synchronizes breakpoints in the + * IDE and breakpoints in the debugger. The IDE breakpoints are managed by the + * {@link IBreakpointManager} while the debugger breakpoints are accessed + * through the {@link IBreakpoints} service. + *

+ * This class is not intended to be extended by clients. Instead clients should + * implement the {@link IBreakpointAttributeTranslator} interface which is used + * to translate breakpoint attributes between the IDE and debugger breakpoints. + *

+ * Note: This breakpoint mediator implementation has been superseded by a more + * powerful {@link BreakpointsMediator2} implementation. * + * @since 1.0 + * @see IBreakpointAttributeTranslator + * @see BreakpointsMediator2 */ public class BreakpointsMediator extends AbstractDsfService implements IBreakpointManagerListener, IBreakpointListener { diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/BreakpointsMediator2.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/BreakpointsMediator2.java new file mode 100644 index 00000000000..77952f8300d --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/BreakpointsMediator2.java @@ -0,0 +1,1140 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008 Wind River 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 - Initial API and implementation + * Ericsson - Low-level breakpoints integration + * Nokia - refactored to work for both GDB and EDC. Nov. 2009. + *******************************************************************************/ + +package org.eclipse.cdt.dsf.debug.service; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Hashtable; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.RejectedExecutionException; + +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.IDsfStatusConstants; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.concurrent.ThreadSafeAndProhibitedFromDsfExecutor; +import org.eclipse.cdt.dsf.debug.service.IBreakpoints.IBreakpointDMContext; +import org.eclipse.cdt.dsf.debug.service.IBreakpoints.IBreakpointsTargetDMContext; +import org.eclipse.cdt.dsf.internal.DsfPlugin; +import org.eclipse.cdt.dsf.service.AbstractDsfService; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.core.resources.IMarkerDelta; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Plugin; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.debug.core.DebugPlugin; +import org.eclipse.debug.core.IBreakpointManager; +import org.eclipse.debug.core.IBreakpointsListener; +import org.eclipse.debug.core.model.IBreakpoint; +import org.osgi.framework.BundleContext; + +/** +/** + * Breakpoints mediator is a DSF service which synchronizes breakpoints in the + * IDE and breakpoints in the debugger. The IDE breakpoints are managed by the + * {@link IBreakpointManager} while the debugger breakpoints are accessed + * through the {@link IBreakpoints} service. + *

+ * This class is not intended to be extended by clients. Instead clients should + * implement the {@link IBreakpointAttributeTranslator2} interface which is used + * to translate breakpoint attributes between the IDE and debugger breakpoints. + *

+ * Note: This breakpoint mediator is a second generation implementation that + * succeeds {@link BreakpointsMediator}. This new implementation includes + * the following additional features: + *

+ * + * @see IBreakpointAttributeTranslator2 + * @see BreakpointsMediator + * + * @since 2.1 + */ +public class BreakpointsMediator2 extends AbstractDsfService implements IBreakpointsListener +{ + public enum BreakpointEventType {ADDED, REMOVED, MODIFIED}; + + /** + * The attribute translator that this service will use to map the platform + * breakpoint attributes to the corresponding target attributes, and vice + * versa. + */ + private IBreakpointAttributeTranslator2 fAttributeTranslator2; + + /** + * DSF Debug service for creating breakpoints. + */ + IBreakpoints fBreakpointsService; + + /** + * Platform breakpoint manager + */ + IBreakpointManager fBreakpointManager; + + /** + * Object describing the information about a single target breakpoint + * corresponding to specific platform breakpoint and breakpoint target + * context. + */ + public interface ITargetBreakpointInfo { + + /** + * Returns the breakpoint attributes as returned by the attribute translator. + */ + public Map getAttributes(); + + /** + * Returns the target breakpoint context. Returns null if the + * breakpoint failed to install on target. + */ + public IBreakpointDMContext getTargetBreakpoint(); + + /** + * Returns the status result of the last breakpoint operation (install/remove). + */ + public IStatus getStatus(); + } + + private static class TargetBP implements ITargetBreakpointInfo { + + private Map fAttributes; + private IBreakpointDMContext fTargetBPContext; + private IStatus fStatus; + + public TargetBP(Map attrs) { + fAttributes = attrs; + } + + public Map getAttributes() { + return fAttributes; + } + + public IBreakpointDMContext getTargetBreakpoint() { + return fTargetBPContext; + } + + public IStatus getStatus() { + return fStatus; + } + + public void setTargetBreakpoint(IBreakpointDMContext fTargetBPContext) { + this.fTargetBPContext = fTargetBPContext; + } + + public void setStatus(IStatus status) { + this.fStatus = status; + } + } + + private class PlatformBreakpointInfo { + IBreakpoint breakpoint; + boolean enabled; + // All attributes available from UI, including standard and extended ones. + Map attributes; + + public PlatformBreakpointInfo(IBreakpoint bp, boolean enabled, Map attributes) { + super(); + breakpoint = bp; + this.enabled = enabled; + this.attributes = attributes; + } + } + + /////////////////////////////////////////////////////////////////////////// + // Breakpoints tracking + /////////////////////////////////////////////////////////////////////////// + + /** + * Holds the set of platform breakpoints with their breakpoint information + * structures, 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>> fPlatformBPs = + new HashMap>>(); + + /** + * Mapping of platform breakpoints to all their attributes (standard ones and + * extended ones) from UI. This will be used to check what attributes have + * changed for a breakpoint when the breakpoint is changed. The map is
+ * 1. augmented in breakpointsAdded();
+ * 2. updated in breakpointsChanged();
+ * 3. diminished in breakpointsRemoved(); + */ + private Map> fBreakpointAttributes = + new HashMap>(); + + /** + * Hold info about a breakpoint events (added, removed, changed) for later + * handling. + */ + private static class PendingEventInfo { + PendingEventInfo(BreakpointEventType eventType, PlatformBreakpointInfo bpInfo, + Collection bpsTargetDmc, RequestMonitor rm) { + fEventType = eventType; + fBPInfo = bpInfo; + fBPTargetContexts = bpsTargetDmc; + fRequestMonitor = rm; + fAttributeDelta = null; + } + + PendingEventInfo(BreakpointEventType eventType, Collection updateContexts, + Map attrDelta) { + fEventType = eventType; + fBPTargetContexts = updateContexts; + fAttributeDelta = attrDelta; + fRequestMonitor = null; + fBPInfo = null; + } + + PlatformBreakpointInfo fBPInfo; + RequestMonitor fRequestMonitor; + BreakpointEventType fEventType; + Collection fBPTargetContexts; + Map fAttributeDelta; // for change event only + } + + /** + * 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 fRunningEvents = new HashSet(); + + private Map> fPendingEvents = + new HashMap>(); + + /////////////////////////////////////////////////////////////////////////// + // AbstractDsfService + /////////////////////////////////////////////////////////////////////////// + + /** + * The service constructor + * + * @param session + * @param debugModelId + */ + public BreakpointsMediator2(DsfSession session, IBreakpointAttributeTranslator2 attributeTranslator) { + super(session); + fAttributeTranslator2 = attributeTranslator; + } + + @Override + public void initialize(final RequestMonitor rm) { + // - Collect references for the services we interact with + // - Register to interesting events + // - Obtain the list of platform breakpoints + // - Register the service for interested parties + super.initialize( + new RequestMonitor(getExecutor(), rm) { + @Override + protected void handleSuccess() { + doInitialize(rm); + }}); + } + + /** + * Asynchronous service initialization + * + * @param requestMonitor + */ + private void doInitialize(RequestMonitor rm) { + + // Get the services references + fBreakpointsService = getServicesTracker().getService(IBreakpoints.class); + fBreakpointManager = DebugPlugin.getDefault().getBreakpointManager(); + fAttributeTranslator2.initialize(this); + + // Register to the useful events + fBreakpointManager.addBreakpointListener(this); + + // Register this service + register(new String[] { BreakpointsMediator2.class.getName() }, + new Hashtable()); + + rm.done(); + } + + @Override + public void shutdown(final RequestMonitor rm) { + // - 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... + + // Stop accepting requests and events + unregister(); + fBreakpointManager.removeBreakpointListener(this); + fAttributeTranslator2.dispose(); + + // 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() { + BreakpointsMediator2.super.shutdown(rm); + } + }; + + // We have to make a copy of the fPlatformBPs keys because uninstallBreakpoints() + // modifies the map as it walks through it. + List platformBPKeysCopy = new ArrayList(fPlatformBPs.size()); + platformBPKeysCopy.addAll(0, fPlatformBPs.keySet()); + for (IBreakpointsTargetDMContext dmc : platformBPKeysCopy) { + stopTrackingBreakpoints(dmc, countingRm); + } + countingRm.setDoneCount(platformBPKeysCopy.size()); + } + + @Override + protected BundleContext getBundleContext() { + return DsfPlugin.getBundleContext(); + } + + protected String getPluginID() { + return DsfPlugin.PLUGIN_ID; + } + + protected Plugin getPlugin() { + return DsfPlugin.getDefault(); + } + + /** + * 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 #stopTrackingBreakpoints} 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) { + // - Augment the maps with the new execution context + // - Install the platform breakpoints on the selected target + + // Make sure a mapping for this execution context does not already exist + Map> platformBPs = fPlatformBPs.get(dmc); + if (platformBPs != null) { + rm.setStatus(new Status(IStatus.ERROR, getPluginID(), INTERNAL_ERROR, "Context already initialized", null)); //$NON-NLS-1$ + 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. + fPlatformBPs.put(dmc, new HashMap>()); + + // Install the platform breakpoints (stored in fPlatformBPs) on the target. + // We need to use a background thread for this operation because we are + // accessing the resources system to retrieve the breakpoint attributes. + // Accessing the resources system potentially requires using global locks. + // Also we will be calling IBreakpointAttributeTranslator which is prohibited + // from being called on the session executor thread. + new Job("Install initial breakpoint list.") { //$NON-NLS-1$ + { setSystem(true); } + + // Get the stored breakpoints from the platform BreakpointManager + // and install them on the target + @Override + protected IStatus run(IProgressMonitor monitor) { + doBreakpointsAdded(DebugPlugin.getDefault().getBreakpointManager().getBreakpoints(), dmc, rm); + return Status.OK_STATUS; + } + }.schedule(); + } + + /** + * Remove and stop installing breakpoints for the given breakpoints target context. + * @param dmc Context to stop tracking breakpoints for. + * @param rm Completion callback. + */ + public void stopTrackingBreakpoints(final IBreakpointsTargetDMContext dmc, final RequestMonitor rm) { + // - Remove the target breakpoints for the given execution context + // - Update the maps + + // Remove the breakpoints for given DMC from the internal maps. + Map> platformBPs = fPlatformBPs.get(dmc); + if (platformBPs == null || platformBPs.size() == 0) { + rm.setStatus(new Status(IStatus.INFO /* NOT error */, getPluginID(), INTERNAL_ERROR, "Breakpoints not installed for given context", null)); //$NON-NLS-1$ + rm.done(); + return; + } + + // Just remove the IBreakpoints installed for the "dmc". + final IBreakpoint[] bps = platformBPs.keySet().toArray(new IBreakpoint[platformBPs.size()]); + + new Job("Uninstall target breakpoints list.") { //$NON-NLS-1$ + { setSystem(true); } + + // Get the stored breakpoints from the platform BreakpointManager + // and install them on the target + @Override + protected IStatus run(IProgressMonitor monitor) { + doBreakpointsRemoved(bps, dmc, rm); + return Status.OK_STATUS; + } + }.schedule(); + } + + /** + * Find target breakpoints installed in the given context that are resolved + * from the given platform breakpoint. + * + * @param dmc - context + * @param platformBp - platform breakpoint + * @return array of target breakpoints. + */ + public ITargetBreakpointInfo[] getTargetBreakpoints(IBreakpointsTargetDMContext dmc, IBreakpoint platformBp) { + assert getExecutor().isInExecutorThread(); + + Map> platformBPs = fPlatformBPs.get(dmc); + + if (platformBPs != null) + { + List bpInfo = platformBPs.get(platformBp); + if (bpInfo != null) { + return bpInfo.toArray(new ITargetBreakpointInfo[bpInfo.size()]); + } + } + return null; + } + + /** + * Find the platform breakpoint that's mapped to the given target breakpoint. + * + * @param dmc - context of the target breakpoint, can be null. + * @param bp - target breakpoint + * @return platform breakpoint. null if not found. + */ + public IBreakpoint getPlatformBreakpoint(IBreakpointsTargetDMContext dmc, IBreakpointDMContext bp) { + assert getExecutor().isInExecutorThread(); + + for (IBreakpointsTargetDMContext bpContext : fPlatformBPs.keySet()) { + if (dmc != null && !dmc.equals(bpContext)) + continue; + + Map> platformBPs = fPlatformBPs.get(bpContext); + + if (platformBPs != null && platformBPs.size() > 0) + { + for(Map.Entry> e: platformBPs.entrySet()) + { + // Stop at the first occurrence + for (TargetBP tbp : e.getValue()) + if(tbp.getTargetBreakpoint().equals(bp)) + return e.getKey(); + } + } + } + + return null; + } + + /////////////////////////////////////////////////////////////////////////// + // Back-end interface functions + /////////////////////////////////////////////////////////////////////////// + + /** + * Install a new platform breakpoint on the back-end. A platform breakpoint + * can resolve into multiple back-end breakpoints, e.g. when threads are taken + * into account. + * + * @param dmc + * @param breakpoint + * @param attrsList - list of attribute map, each mapping to a potential target BP. + * @param rm + */ + private void installBreakpoint(IBreakpointsTargetDMContext dmc, final IBreakpoint breakpoint, + final List> attrsList, final DataRequestMonitor> rm) + { + // Retrieve the set of breakpoints for this context + final Map> platformBPs = fPlatformBPs.get(dmc); + assert platformBPs != null; + + // Ensure the breakpoint is not already installed + assert !platformBPs.containsKey(breakpoint); + + final ArrayList targetBPsAttempted = new ArrayList(attrsList.size()); + for (int i = 0; i < attrsList.size(); i++) { + targetBPsAttempted.add(new TargetBP(attrsList.get(i))); + } + + final ArrayList targetBPsInstalled = new ArrayList(attrsList.size()); + + // Update the breakpoint status when all back-end breakpoints have been installed + final CountingRequestMonitor installRM = new CountingRequestMonitor(getExecutor(), rm) { + @Override + protected void handleCompleted() { + // Store successful targetBPs with the platform breakpoint + if (targetBPsInstalled.size() > 0) + platformBPs.put(breakpoint, targetBPsInstalled); + + // Store all targetBPs, success or failure, in the rm. + rm.setData(targetBPsAttempted); + rm.done(); + } + }; + + // A back-end breakpoint needs to be installed for each specified attributes map. + installRM.setDoneCount(attrsList.size()); + + // Install the back-end breakpoint(s) + for (int _i = 0; _i < attrsList.size(); _i++) { + final int i = _i; + fBreakpointsService.insertBreakpoint( + dmc, attrsList.get(i), + new DataRequestMonitor(getExecutor(), installRM) { + @Override + protected void handleCompleted() { + TargetBP targetBP = targetBPsAttempted.get(i); + if (isSuccess()) { + // Add the breakpoint back-end mapping + targetBP.setTargetBreakpoint(getData()); + + targetBPsInstalled.add(targetBP); + } + targetBP.setStatus(getStatus()); + + installRM.done(); + } + }); + } + } + + /** + * Un-install an individual breakpoint on the back-end. For one platform + * breakpoint, there could be multiple corresponding back-end breakpoints. + * + * @param dmc + * @param breakpoint + * @param drm + */ + private void uninstallBreakpoint(final IBreakpointsTargetDMContext dmc, final IBreakpoint breakpoint, + final DataRequestMonitor> drm) + { + // Remove the back-end breakpoints + final Map> platformBPs = fPlatformBPs.get(dmc); + if (platformBPs == null) { + drm.setStatus(new Status(IStatus.ERROR, getPluginID(), INVALID_HANDLE, "Invalid breakpoint", null)); //$NON-NLS-1$ + drm.done(); + return; + } + + final List bpList = platformBPs.get(breakpoint); + assert bpList != null; + + // Only try to remove those targetBPs that are successfully installed. + + // Remove completion monitor + final CountingRequestMonitor countingRm = new CountingRequestMonitor(getExecutor(), drm) { + @Override + protected void handleCompleted() { + platformBPs.remove(breakpoint); + + // Complete the request monitor. + drm.setData(bpList); + drm.done(); + } + }; + + int count = 0; + for (int i = 0; i < bpList.size(); i++) { + final TargetBP bp = bpList.get(i); + if (bp.getTargetBreakpoint() != null) { + fBreakpointsService.removeBreakpoint( + bp.getTargetBreakpoint(), + new RequestMonitor(getExecutor(), countingRm) { + @Override + protected void handleCompleted() { + bp.setStatus(getStatus()); + if (isSuccess()) { + bp.setTargetBreakpoint(null); + } + countingRm.done(); + } + }); + count++; + } else { + bp.setStatus(Status.OK_STATUS); + } + } + countingRm.setDoneCount(count); + } + + /////////////////////////////////////////////////////////////////////////// + // IBreakpointManagerListener implementation + /////////////////////////////////////////////////////////////////////////// + + /** + * @noreference This method is not intended to be referenced by clients. + */ + @ThreadSafeAndProhibitedFromDsfExecutor("getExecutor()") + public void breakpointsAdded(final IBreakpoint[] bps) { + doBreakpointsAdded(bps, null, null); + } + + protected void doBreakpointsAdded(final IBreakpoint[] bps, final IBreakpointsTargetDMContext bpsTargetDmc, final RequestMonitor rm) { + // Collect attributes (which will access system resource) + // in non DSF dispatch thread. + // + final PlatformBreakpointInfo[] bpsInfo = collectBreakpointsInfo(bps); + + // Nothing to do + if (bpsInfo.length == 0) { + if (rm != null) { + rm.done(); + } + return; + } + + try { + getExecutor().execute(new DsfRunnable() { + public void run() { + Collection dmcs = new ArrayList(); + if (bpsTargetDmc == null) + dmcs.addAll(fPlatformBPs.keySet()); + else + dmcs.add(bpsTargetDmc); + + doBreakpointsAddedInExecutor(bpsInfo, dmcs, rm); + } + }); + } catch (RejectedExecutionException e) { + IStatus status = new Status(IStatus.ERROR, getPluginID(), IDsfStatusConstants.INTERNAL_ERROR, "Request for monitor: '" + toString() + "' resulted in a rejected execution exception.", e);//$NON-NLS-1$ //$NON-NLS-2$ + if (rm != null) { + rm.setStatus(status); + rm.done(); + } else { + getPlugin().getLog().log(status); + } + } + } + + /** + * Collect breakpoint info. This method must not be called in DSF dispatch thread. + * @param bps + * @return + */ + private PlatformBreakpointInfo[] collectBreakpointsInfo(IBreakpoint[] bps) { + List bpsInfo = new ArrayList(bps.length); + + for (IBreakpoint bp : bps) { + if (bp.getMarker() == null) + continue; + + if (fAttributeTranslator2.supportsBreakpoint(bp)) { + try { + Map attrs = fAttributeTranslator2.getAllBreakpointAttributes(bp, fBreakpointManager.isEnabled()); + boolean enabled = bp.isEnabled() && fBreakpointManager.isEnabled(); + bpsInfo.add(new PlatformBreakpointInfo(bp, enabled, attrs)); + } catch (CoreException e) { + getPlugin().getLog().log(e.getStatus()); + } + } + } + + return bpsInfo.toArray(new PlatformBreakpointInfo[bpsInfo.size()]); + } + + protected void doBreakpointsAddedInExecutor(PlatformBreakpointInfo[] bpsInfo, Collection bpTargetDMCs, final RequestMonitor rm) { + final Map> eventBPs = + new HashMap>(bpsInfo.length, 1); + + CountingRequestMonitor processPendingCountingRm = new CountingRequestMonitor(getExecutor(), rm) { + @Override + protected void handleCompleted() { + processPendingRequests(); + fireUpdateBreakpointsStatus(eventBPs, BreakpointEventType.ADDED); + if (rm != null) + // don't call this if "rm" is null as this will + // log errors if any and pack Eclipse error + // log view with errors meaningless to user. + super.handleCompleted(); + } + }; + int processPendingCountingRmCount = 0; + + for (final PlatformBreakpointInfo bpinfo : bpsInfo) { + final Map targetBPs = + new HashMap(fPlatformBPs.size(), 1); + eventBPs.put(bpinfo.breakpoint, targetBPs); + + // Remember the new attributes of the bp in our global buffer, + // even if we cannot or fail to install the bp. + fBreakpointAttributes.put(bpinfo.breakpoint, bpinfo.attributes); + + if (fRunningEvents.contains(bpinfo.breakpoint)) { + PendingEventInfo pendingEvent = new PendingEventInfo(BreakpointEventType.ADDED, bpinfo, bpTargetDMCs, processPendingCountingRm); + processPendingCountingRmCount++; + updatePendingRequest(bpinfo.breakpoint, pendingEvent); + continue; + } + + processPendingCountingRmCount++; + + // Mark the breakpoint as being updated and go + fRunningEvents.add(bpinfo.breakpoint); + + final CountingRequestMonitor bpTargetsCountingRm = new CountingRequestMonitor(getExecutor(), processPendingCountingRm) { + @Override + protected void handleCompleted() { + // Indicate that the running event has completed + fRunningEvents.remove(bpinfo.breakpoint); + super.handleCompleted(); + } + }; + + int bpTargetsCountingRmCount = 0; + + // Install the breakpoint in all the execution contexts + for (final IBreakpointsTargetDMContext dmc : bpTargetDMCs) { + + // Now ask lower level to set the bp. + // + // if the breakpoint is disabled, ask back-end if it can set (and manage) + // disabled breakpoint. If not, just bail out. + // + if (! bpinfo.enabled) { + Map attr = new HashMap(1); + attr.put(IBreakpoint.ENABLED, Boolean.FALSE); + Map targetEnablementAttr = fAttributeTranslator2.convertAttributes(attr); + if (! fAttributeTranslator2.canUpdateAttributes(bpinfo.breakpoint, dmc, targetEnablementAttr)) { + // bail out. Continue with the next dmc & breakpoint. + continue; + } + } + + // Now do the real work. + // + fAttributeTranslator2.resolveBreakpoint(dmc, bpinfo.breakpoint, bpinfo.attributes, + new DataRequestMonitor>>(getExecutor(), bpTargetsCountingRm){ + @Override + protected void handleSuccess() { + installBreakpoint( + dmc, bpinfo.breakpoint, getData(), + new DataRequestMonitor>(getExecutor(), bpTargetsCountingRm) { + @Override + protected void handleSuccess() { + targetBPs.put(dmc, getData().toArray(new ITargetBreakpointInfo[getData().size()])); + super.handleSuccess(); + }; + }); + }}); + + bpTargetsCountingRmCount++; + } + + bpTargetsCountingRm.setDoneCount(bpTargetsCountingRmCount); + } + + processPendingCountingRm.setDoneCount(processPendingCountingRmCount); + } + + /** + * @noreference This method is not intended to be referenced by clients. + */ + @ThreadSafeAndProhibitedFromDsfExecutor("getExecutor()") + public void breakpointsChanged(IBreakpoint[] bps, IMarkerDelta[] deltas) { + if (fAttributeTranslator2 == null) + return; + + final PlatformBreakpointInfo[] bpsInfo = collectBreakpointsInfo(bps); + + if (bpsInfo.length == 0) + return; // nothing to do + + try { + getExecutor().execute( new DsfRunnable() { + public void run() { + Map tmp = new HashMap(1); + tmp.put(IBreakpoint.ENABLED, true); + final String targetEnablementKey = fAttributeTranslator2.convertAttributes(tmp).keySet().iterator().next(); + + for (PlatformBreakpointInfo bpinfo : bpsInfo) { + /* + * We cannot depend on "deltas" for attribute change. + * For instance, delta can be null when extended + * attributes (e.g. breakpoint thread filter for GDB) + * are changed. + */ + Map newAttrs = bpinfo.attributes; + Map oldAttrs = fBreakpointAttributes.get(bpinfo.breakpoint); + + // remember the new attributes. + fBreakpointAttributes.put(bpinfo.breakpoint, newAttrs); + + if (oldAttrs == null) + continue; + + final Map attrDelta = getAttributesDelta(oldAttrs, newAttrs); + if (attrDelta.size() == 0) + continue; + + final List reinstallContexts = new ArrayList(); + + List updateContexts = new ArrayList(); + + // Now change the breakpoint for each known context. + // + for (final IBreakpointsTargetDMContext btContext : fPlatformBPs.keySet()) { + + if (! fAttributeTranslator2.canUpdateAttributes(bpinfo.breakpoint, btContext, attrDelta)) { + // backend cannot handle at least one of the platform BP attribute change, + // we'll re-install the bp. + reinstallContexts.add(btContext); + } + else { + // Backend claims it can handle the attributes change, let it do it. + updateContexts.add(btContext); + } + + } + + final PlatformBreakpointInfo[] oneBPInfo = new PlatformBreakpointInfo[] {bpinfo}; + IBreakpoint[] oneBP = new IBreakpoint[] {bpinfo.breakpoint}; + + if (reinstallContexts.size() > 0) { + // Check if it's only enablement change (user click enable/disable + // button or "Skip all breakpoints" button), which is common operation. + // + if (attrDelta.size() == 1 && attrDelta.containsKey(targetEnablementKey)) { // only enablement changed. + if (bpinfo.enabled) { + // change from disable to enable. Install the bp. + doBreakpointsAddedInExecutor(oneBPInfo, reinstallContexts, null); + } + else { + // change from enable to disable. Remove the bp. + doBreakpointsRemovedInExecutor(oneBP, reinstallContexts, null); + } + } + else { + doBreakpointsRemovedInExecutor(oneBP, reinstallContexts, new RequestMonitor(getExecutor(), null) { + // What should we do if removal of some or all targetBP fails ? + // Go on with the installation of new targetBPs and let clients (i.e. AttributeTranslators) + // handle the errors. + @Override + protected void handleCompleted() { + doBreakpointsAddedInExecutor(oneBPInfo, reinstallContexts, null); + }}); + } + } + + if (updateContexts.size() > 0) + modifyTargetBreakpoints(bpinfo.breakpoint, updateContexts, attrDelta); + } + } + }); + } catch (RejectedExecutionException e) { + getPlugin().getLog().log(new Status(IStatus.ERROR, getPluginID(), IDsfStatusConstants.INTERNAL_ERROR, "Request for monitor: '" + toString() + "' resulted in a rejected execution exception.", e)); //$NON-NLS-1$ //$NON-NLS-2$ + } + + } + + /** + * For the given platform BP, ask the backend to modify all its target BPs + * with the given attribute change.
+ * This must be called in DSF executor thread. + * + * @param bp + * @param updateContexts + * target contexts in which to do the modification. + * @param targetAttrDelta + * target-recognizable attribute(s) with new values. + */ + private void modifyTargetBreakpoints(final IBreakpoint bp, Collection updateContexts, Map targetAttrDelta) { + // If the breakpoint is currently being updated, queue the request and exit + if (fRunningEvents.contains(bp)) { + PendingEventInfo pendingEvent = new PendingEventInfo(BreakpointEventType.MODIFIED, updateContexts, targetAttrDelta); + updatePendingRequest(bp, pendingEvent); + return; + } + + CountingRequestMonitor modifyTargetBPCRM = new CountingRequestMonitor(getExecutor(), null) { + @Override + protected void handleCompleted() { + fRunningEvents.remove(bp); + }}; + + int targetBPCount = 0; + + fRunningEvents.add(bp); + + for (IBreakpointsTargetDMContext context : updateContexts) { + List targetBPs = fPlatformBPs.get(context).get(bp); + if (targetBPs != null) { + for (TargetBP tbp : targetBPs) { + // this must be an installed breakpoint. + assert (tbp.getTargetBreakpoint() != null); + + targetBPCount++; + fBreakpointsService.updateBreakpoint(tbp.getTargetBreakpoint(), targetAttrDelta, modifyTargetBPCRM); + } + } + } + + modifyTargetBPCRM.setDoneCount(targetBPCount); + } + + /** + * @noreference This method is not intended to be referenced by clients. + */ + @ThreadSafeAndProhibitedFromDsfExecutor("getExecutor()") + public void breakpointsRemoved(final IBreakpoint[] bps, IMarkerDelta delta[]) { + getExecutor().execute(new DsfRunnable() { + public void run() { + for (IBreakpoint bp : bps) + fBreakpointAttributes.remove(bp); + } + }); + + doBreakpointsRemoved(bps, null, null); + } + + protected void doBreakpointsRemoved(final IBreakpoint[] bps, final IBreakpointsTargetDMContext bpsTargetDmc, final RequestMonitor rm) { + + final List bpCandidates = new ArrayList(); + + for (int i = 0; i < bps.length; i++) { + IBreakpoint bp = bps[i]; + + if (fAttributeTranslator2.supportsBreakpoint(bp)) { + bpCandidates.add(bp); + } + } + + if (bpCandidates.isEmpty()) { // nothing to do + if (rm != null) + rm.done(); + return; + } + + try { + getExecutor().execute(new DsfRunnable() { + public void run() { + Collection contexts = new ArrayList(); + if (bpsTargetDmc == null) + contexts.addAll(fPlatformBPs.keySet()); + else + contexts.add(bpsTargetDmc); + + doBreakpointsRemovedInExecutor(bpCandidates.toArray(new IBreakpoint[bpCandidates.size()]), contexts, rm); + } + }); + } catch (RejectedExecutionException e) { + IStatus status = new Status(IStatus.ERROR, getPluginID(), IDsfStatusConstants.INTERNAL_ERROR, + "Request for monitor: '" + toString() + "' resulted in a rejected execution exception.", e);//$NON-NLS-1$ //$NON-NLS-2$ + if (rm != null) { + rm.setStatus(status); + rm.done(); + } else { + getPlugin().getLog().log(status); + } + } + } + + protected void doBreakpointsRemovedInExecutor(IBreakpoint[] bpCandidates, + Collection targetContexts, final RequestMonitor rm) { + + final Map> eventBPs = + new HashMap>(bpCandidates.length, 1); + + CountingRequestMonitor processPendingCountingRm = new CountingRequestMonitor(getExecutor(), rm) { + @Override + protected void handleCompleted() { + processPendingRequests(); + fireUpdateBreakpointsStatus(eventBPs, BreakpointEventType.REMOVED); + if (rm != null) + // don't call this if "rm" is null as this will + // log errors if any and pack Eclipse error + // log view with errors meaningless to user. + super.handleCompleted(); + } + }; + int processPendingCountingRmCount = 0; + + for (final IBreakpoint breakpoint : bpCandidates) { + + // If the breakpoint is currently being updated, queue the request and exit + if (fRunningEvents.contains(breakpoint)) { + PendingEventInfo pendingEvent = new PendingEventInfo(BreakpointEventType.REMOVED, null, targetContexts, processPendingCountingRm); + processPendingCountingRmCount++; + updatePendingRequest(breakpoint, pendingEvent); + continue; // handle next breakpoint + } + + processPendingCountingRmCount++; + + final Map targetBPs = + new HashMap(fPlatformBPs.size(), 1); + eventBPs.put(breakpoint, targetBPs); + + CountingRequestMonitor bpTargetsCountingRM = new CountingRequestMonitor(getExecutor(), processPendingCountingRm) { + @Override + protected void handleCompleted() { + // Indicate that the running event has completed + fRunningEvents.remove(breakpoint); + super.handleCompleted(); + } + }; + + int bpTargetsCoutingRMCount = 0; + + // Mark the breakpoint as being updated and go + fRunningEvents.add(breakpoint); + + // Remove the breakpoint in all the execution contexts + for (final IBreakpointsTargetDMContext dmc : targetContexts) { + + if (fPlatformBPs.get(dmc).containsKey(breakpoint)) { // there are targetBPs installed + // now do time-consuming part of the work. + + uninstallBreakpoint( + dmc, breakpoint, + new DataRequestMonitor>(getExecutor(), bpTargetsCountingRM) { + @Override + protected void handleSuccess() { + targetBPs.put(dmc, getData().toArray(new ITargetBreakpointInfo[getData().size()])); + super.handleSuccess(); + }; + }); + bpTargetsCoutingRMCount++; + } else { + // Breakpoint not installed for given context, do nothing. + } + } + + bpTargetsCountingRM.setDoneCount(bpTargetsCoutingRMCount); + } + + processPendingCountingRm.setDoneCount(processPendingCountingRmCount); + } + + private void updatePendingRequest(IBreakpoint breakpoint, PendingEventInfo pendingEvent) { + LinkedList pendingEventsList = fPendingEvents.get(breakpoint); + if (pendingEventsList == null) { + pendingEventsList = new LinkedList(); + fPendingEvents.put(breakpoint, pendingEventsList); + } + if (pendingEventsList.size() > 0 && + pendingEventsList.getLast().fEventType == BreakpointEventType.MODIFIED) { + pendingEventsList.removeLast(); + } + pendingEventsList.add(pendingEvent); + } + + private void processPendingRequests() { + /* + * This will process only first pending request for each breakpoint, + * whose RequestMonitor (see "processPendingCountingRm" in such methods as + * doBreakpointsRemovedInExecutor()) will invoke this method again. + */ + if (fPendingEvents.isEmpty()) return; // Nothing to do + + // Make a copy to avoid ConcurrentModificationException + // as we are deleting element in the loop. + Set bpsInPendingEvents = new HashSet(fPendingEvents.keySet()); + for (IBreakpoint bp : bpsInPendingEvents) { + if (! fRunningEvents.contains(bp)) { + LinkedList eventInfoList = fPendingEvents.get(bp); + + // Process the first pending request for this breakpoint + PendingEventInfo eventInfo = eventInfoList.removeFirst(); + + if (eventInfoList.isEmpty()) + fPendingEvents.remove(bp); + + switch (eventInfo.fEventType) { + case ADDED: + doBreakpointsAddedInExecutor(new PlatformBreakpointInfo[] {eventInfo.fBPInfo}, eventInfo.fBPTargetContexts, eventInfo.fRequestMonitor); + break; + case MODIFIED: + modifyTargetBreakpoints(bp, eventInfo.fBPTargetContexts, eventInfo.fAttributeDelta); + break; + case REMOVED: + doBreakpointsRemovedInExecutor(new IBreakpoint[]{bp}, eventInfo.fBPTargetContexts, eventInfo.fRequestMonitor); + break; + } + } + } + } + + private void fireUpdateBreakpointsStatus(final Map> eventBPs, final BreakpointEventType eventType) { + // Update breakpoint status + new Job("Breakpoint status update") { //$NON-NLS-1$ + { setSystem(true); } + @Override + protected IStatus run(IProgressMonitor monitor) { + fAttributeTranslator2.updateBreakpointsStatus(eventBPs, eventType); + return Status.OK_STATUS; + }; + }.schedule(); + + } + + /** + * Determine the set of modified attributes. + * + * @param oldAttributes old map of attributes. + * @param newAttributes new map of attributes. + * @return new and changed attribute in the new map. May be empty indicating the two maps are equal. + */ + private Map getAttributesDelta(Map oldAttributes, Map newAttributes) { + + Map delta = new HashMap(); + + Set oldKeySet = oldAttributes.keySet(); + Set newKeySet = newAttributes.keySet(); + + Set commonKeys = new HashSet(newKeySet); commonKeys.retainAll(oldKeySet); + Set addedKeys = new HashSet(newKeySet); addedKeys.removeAll(oldKeySet); + Set 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 delta; + } +} diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IBreakpointAttributeTranslator.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IBreakpointAttributeTranslator.java index 3d7761d0d37..e17a086cb7f 100644 --- a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IBreakpointAttributeTranslator.java +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IBreakpointAttributeTranslator.java @@ -13,29 +13,62 @@ package org.eclipse.cdt.dsf.debug.service; import java.util.List; import java.util.Map; +import org.eclipse.cdt.dsf.concurrent.ConfinedToDsfExecutor; import org.eclipse.cdt.dsf.concurrent.ThreadSafeAndProhibitedFromDsfExecutor; import org.eclipse.cdt.dsf.debug.service.IBreakpoints.IBreakpointDMContext; import org.eclipse.core.runtime.CoreException; import org.eclipse.debug.core.model.IBreakpoint; /** - * Breakpoint attribute translator interface + * Breakpoint attribute translator is used by the {@link BreakpointsMediator} + * to map IDE breakpoint attributes to debugger breakpoint attributes. + *

+ * Note: The attribute translator is expected to access IDE breakpoint manager + * objects which are synchronized using the resource system. Therefore all the + * translator methods are called using background threads. When the attribute + * translator needs to access DSF services, it needs to schedule a runnable using + * the DSF session executable. * + * @see BreakpointMediator * @since 1.0 */ - @ThreadSafeAndProhibitedFromDsfExecutor("") public interface IBreakpointAttributeTranslator { - public void initialize(BreakpointsMediator mediator); - - public void dispose(); - - public List> getBreakpointAttributes(IBreakpoint breakpoint, boolean bpManagerEnabled) throws CoreException; - - public boolean canUpdateAttributes(IBreakpointDMContext bp, Map delta); + /** + * Initializes the translator. This method is called by the breakpoint + * mediator service, when the mediator service is initialized. + */ + @ConfinedToDsfExecutor("") + public void initialize(BreakpointsMediator mediator); + /** + * Disposes the translator. Also called by the mediator upon service + * shutdown. + */ + @ConfinedToDsfExecutor("") + public void dispose(); + + /** + * Returns whether the given IDE breakpoint is supported by this debugger. + */ public boolean supportsBreakpoint(IBreakpoint bp); + /** + * Returns the target breakpoint attributes for the given IDE breakpoint. + */ + public List> getBreakpointAttributes(IBreakpoint breakpoint, boolean bpManagerEnabled) throws CoreException; + + /** + * Based on the given change in IDE breakpoint attributes, this method returns + * whether the given target breakpoint can be modified using + * {@link IBreakpoints#updateBreakpoint(IBreakpointDMContext, Map, org.eclipse.cdt.dsf.concurrent.RequestMonitor)} + * method. + */ + public boolean canUpdateAttributes(IBreakpointDMContext bp, Map delta); + + /** + * Notifies the translator to update the given IDE breakpoint's status. + */ public void updateBreakpointStatus(IBreakpoint bp); } \ No newline at end of file diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IBreakpointAttributeTranslator2.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IBreakpointAttributeTranslator2.java new file mode 100644 index 00000000000..e76da5827b0 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IBreakpointAttributeTranslator2.java @@ -0,0 +1,154 @@ +/******************************************************************************* + * Copyright (c) 2008 Wind River Systems 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 + * Nokia - enhanced to work for both GDB and EDC. Nov. 2009. + *******************************************************************************/ +package org.eclipse.cdt.dsf.debug.service; + +import java.util.List; +import java.util.Map; + +import org.eclipse.cdt.dsf.concurrent.ConfinedToDsfExecutor; +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.ThreadSafe; +import org.eclipse.cdt.dsf.concurrent.ThreadSafeAndProhibitedFromDsfExecutor; +import org.eclipse.cdt.dsf.debug.service.BreakpointsMediator2.BreakpointEventType; +import org.eclipse.cdt.dsf.debug.service.BreakpointsMediator2.ITargetBreakpointInfo; +import org.eclipse.cdt.dsf.debug.service.IBreakpoints.IBreakpointsTargetDMContext; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.debug.core.model.IBreakpoint; + +/** + * Breakpoint attribute translator is used by the {@link BreakpointsMediator2} + * to map IDE breakpoint attributes to debugger breakpoint attributes. + *

+ * Note: The attribute translator is expected to access IDE breakpoint manager + * objects which are synchronized using the resource system. It may also need to + * access DSF services using the DSF session executor. Therefore, the + * implementation needs to pay special attention to the synchronization + * annotations on each method of the interface. + * + * @see BreakpointMediator + * @since 2.1 + */ +public interface IBreakpointAttributeTranslator2 { + + /** + * Initializes the translator. This method is called by the breakpoint + * mediator service, when the mediator service is initialized. + */ + @ConfinedToDsfExecutor("") + public void initialize(BreakpointsMediator2 mediator); + + /** + * Disposes the translator. Also called by the mediator upon service + * shutdown. + */ + @ConfinedToDsfExecutor("") + public void dispose(); + + /** + * Returns whether the given IDE breakpoint is supported by this debugger. + */ + @ThreadSafeAndProhibitedFromDsfExecutor("") + public boolean supportsBreakpoint(IBreakpoint bp); + + /** + * Resolve the breakpoint in given context. A platform BP may be mapped to + * ont or more target BPs, e.g. a breakpoint in an in-line function may be + * mapped to several target BPs, or a thread-specific BP may be mapped to + * several target BPs each of which is for one thread. This method will get + * the list of attribute maps each of which corresponds to one target BP. + *

+ * This method is and must be called in DSF execution thread. + * + * @param context + * - a IBreakpointsTargetDMContext object (which could be a + * process or a loaded module) in which we locate target BPs for + * the platform BP. Cannot be null. + * @param breakpoint + * - platform breakpoint. + * @param bpAttributes + * - all attributes of the breakpoint, usually output from + * {@link #getAllBreakpointAttributes(IBreakpoint, boolean)}. + * @param drm + * - on completion of the request, the DataRequestMonitor + * contains one or more attribute maps each of which + * corresponding to one target breakpoint. + * @throws CoreException + */ + @ConfinedToDsfExecutor("") + public void resolveBreakpoint(IBreakpointsTargetDMContext context, IBreakpoint breakpoint, + Map bpAttributes, DataRequestMonitor>> drm); + + /** + * Get all platform defined attributes for a breakpoint plus all attributes + * defined by any breakpoint extension such as ICBreakpointExtension in CDT. + * In other words, get all attributes available from UI. + *

+ * The attributes returned are independent of runtime context (process, + * module, etc), whereas attributes from + * {@link #resolveBreakpoint(IBreakpointsTargetDMContext, IBreakpoint, Map, DataRequestMonitor)} + * are context sensitive. + *

+ * Note this method must not be called in DSF dispatch thread because we are + * accessing the resources system to retrieve the breakpoint attributes. + * Accessing the resources system potentially requires using global locks. + * + * @param platformBP + * @param bpManagerEnabled + * @return list of target (debugger implementation) recognizable attributes. + * @throws CoreException + */ + @ThreadSafeAndProhibitedFromDsfExecutor("") + public Map getAllBreakpointAttributes(IBreakpoint platformBP, boolean bpManagerEnabled) throws CoreException; + + /** + * Convert platform breakpoint attributes to target attributes. This usually + * involves changing attributes keys to target recognizable ones. For + * instance, GDB integration has its own breakpoint attribute keys. + *

+ * This method overlaps somewhat with the {@link #resolveBreakpoint} + * method. However, this method is currently only used by the mediator in + * conjunction with the {@link #canUpdateAttributes} method. + * + * @param platformBPAttr + * @return + */ + @ThreadSafe + public Map convertAttributes(Map platformBPAttr); + + /** + * Update platform about breakpoint status change, e.g. breakpoint installed on target successfully or breakpoint + * removed from target successfully. + *

+ * Note this method is not and must not be called in DSF dispatch thread. + * + * @param bpsInfo + * @param eventType + */ + @ThreadSafeAndProhibitedFromDsfExecutor("") + public void updateBreakpointsStatus(Map> bpsInfo, BreakpointEventType eventType); + + /** + * This is enhanced version of + * {@link #canUpdateAttributes(org.eclipse.cdt.dsf.debug.service.IBreakpoints.IBreakpointDMContext, Map)} + * in that this API gives more context parameters so that client can make + * decision on a finer granularity when needed. + *

+ * This will be called in DSF dispatch thread. + * + * @param bp platform breakpoint. + * @param context + * @param attributes target-recognizable attributes. + * @return false as long as one of the attributes cannot be updated by client, otherwise true. + */ + @ConfinedToDsfExecutor("") + public boolean canUpdateAttributes(IBreakpoint bp, IBreakpointsTargetDMContext context, Map attributes); +} \ No newline at end of file