# HG changeset patch # User l12wang # Date 1257972510 21600 # Node ID 6b1088abccf89f40ab30eb646c9c83c35b682c11 # Parent c2563c41652554d397a38b5cd7c9466d1b9af727 Reworked BreakpointsMediator. See Eclipse bug 292468. diff -r c2563c416525 -r 6b1088abccf8 cdt/cdt_6_0_x/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/BreakpointsMediator.java --- a/cdt/cdt_6_0_x/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/BreakpointsMediator.java Tue Nov 10 09:58:50 2009 -0600 +++ b/cdt/cdt_6_0_x/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/BreakpointsMediator.java Wed Nov 11 14:48:30 2009 -0600 @@ -13,6 +13,7 @@ package org.eclipse.cdt.dsf.debug.service; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Hashtable; @@ -25,11 +26,11 @@ 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.ImmediateExecutor; 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.IBreakpointAttributeTranslator.EBreakpointStatusChange; 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; @@ -42,24 +43,31 @@ import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; 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.IBreakpointsListener; import org.eclipse.debug.core.model.IBreakpoint; import org.osgi.framework.BundleContext; /** * */ -public class BreakpointsMediator extends AbstractDsfService implements IBreakpointManagerListener, IBreakpointListener +public class BreakpointsMediator extends AbstractDsfService implements IBreakpointManagerListener, IBreakpointsListener { - + public enum BreakpointEventType {ADDED, REMOVED, MODIFIED}; + /** * The attribute translator that this service will use to map the platform - * breakpiont attributes to the corresponding target attributes, and vice + * breakpoint attributes to the corresponding target attributes, and vice * versa. */ private IBreakpointAttributeTranslator fAttributeTranslator; + + /** + * If the attribute translator implements the {@link IBreakpointAttributeTranslatorExtension}, + * this field will be valid, otherwise it is null. + */ + private IBreakpointAttributeTranslatorExtension fAttributeTranslator2; /** * DSF Debug service for creating breakpoints. @@ -70,48 +78,105 @@ * Platform breakpoint manager */ IBreakpointManager fBreakpointManager; - + + /** + * Object describing the information about a single target breakpoint + * corresponding to specific platform breakpoint and breakpoint target + * context. + * + * @since 2.1 + */ + public interface ITargetBreakpointInfo { + + /** + * Returns the breakpoint attributes as returned by the attribute translator. + */ + public Map getAttributes(); + + /** + * Returns the target breakpoint context. May be 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 Map fAttributesDelta; // not really useful ? + 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; + } + } /////////////////////////////////////////////////////////////////////////// // Breakpoints tracking /////////////////////////////////////////////////////////////////////////// /** - * Holds the set of platform breakpoints with their corresponding back-end - * breakpoint attributes, per context (i.e. each platform breakpoint is + * 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>>>(); - - /** - * 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>> fBreakpointDMContexts = - new HashMap>>(); + private Map>> fPlatformBPs = + new HashMap>>(); /** * 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 fPendingRequests = new HashSet(); + private Set fRunningEvents = new HashSet(); + + private static class PendingEventInfo { + PendingEventInfo(BreakpointEventType eventType, IBreakpointsTargetDMContext bpsTargetDmc, RequestMonitor rm) { + fEventType = eventType; + fBPsTargetDmc = bpsTargetDmc; + fRequestMonitor = rm; + } + + RequestMonitor fRequestMonitor; + BreakpointEventType fEventType; + IBreakpointsTargetDMContext fBPsTargetDmc; + } /** * @see fPendingRequests */ - private Set fPendingBreakpoints = new HashSet(); + private Map> fPendingEvents = + Collections.synchronizedMap(new HashMap>()); /////////////////////////////////////////////////////////////////////////// // AbstractDsfService @@ -126,6 +191,10 @@ public BreakpointsMediator(DsfSession session, IBreakpointAttributeTranslator attributeTranslator) { super(session); fAttributeTranslator = attributeTranslator; + + fAttributeTranslator2 = null; + if (attributeTranslator instanceof IBreakpointAttributeTranslatorExtension) + fAttributeTranslator2 = (IBreakpointAttributeTranslatorExtension)attributeTranslator; } @Override @@ -218,20 +287,12 @@ * @param dmc Context to start tracking breakpoints for. * @param rm Completion callback. */ - public void startTrackingBreakpoints(IBreakpointsTargetDMContext dmc, final RequestMonitor rm) { + 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 - - // Validate the context - final IBreakpointsTargetDMContext breakpointsDmc = DMContexts.getAncestorOfType(dmc, IBreakpointsTargetDMContext.class); - if (breakpointsDmc == null) { - rm.setStatus(new Status(IStatus.ERROR, DsfPlugin.PLUGIN_ID, INTERNAL_ERROR, "Invalid context type", null)); //$NON-NLS-1$ - rm.done(); - return; - } // Make sure a mapping for this execution context does not already exist - Map>> platformBPs = fPlatformBPs.get(dmc); + Map> platformBPs = fPlatformBPs.get(dmc); if (platformBPs != null) { rm.setStatus(new Status(IStatus.ERROR, DsfPlugin.PLUGIN_ID, INTERNAL_ERROR, "Context already initialized", null)); //$NON-NLS-1$ rm.done(); @@ -240,8 +301,7 @@ // 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(breakpointsDmc, new HashMap>>()); - fBreakpointDMContexts.put(breakpointsDmc, new HashMap>()); + 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 @@ -249,147 +309,135 @@ // 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("MI Debugger: Install initial breakpoint list.") { //$NON-NLS-1$ + 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(); + } + + 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) { + rm.setStatus(new Status(IStatus.ERROR, DsfPlugin.PLUGIN_ID, INTERNAL_ERROR, "Breakpoints not installed for given context", null)); //$NON-NLS-1$ + rm.done(); + return; + } + + 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) { - // Read initial breakpoints from platform. Copy the breakpoint attributes into a local map. - // Note that we cannot write data into fPlatformBPs table here directly because we are not - // executing on the dispatch thread. - final Map>> initialPlatformBPs = - new HashMap>>(); - try { - // Get the stored breakpoint list from the platform BreakpointManager - IBreakpoint[] bps = DebugPlugin.getDefault().getBreakpointManager().getBreakpoints(); - // Single out the installable breakpoints... - for (IBreakpoint bp : bps) { - if (fAttributeTranslator.supportsBreakpoint(bp)) { - // Retrieve the breakpoint attributes - List> attrsArray = - fAttributeTranslator.getBreakpointAttributes(bp, fBreakpointManager.isEnabled()); - // Store it for now (will be installed on the dispatcher thread) - initialPlatformBPs.put(bp, attrsArray); - } - } - } catch (CoreException e) { - IStatus status = new Status( - IStatus.ERROR, DsfPlugin.PLUGIN_ID, REQUEST_FAILED, "Unable to read initial breakpoint attributes", e); //$NON-NLS-1$ - rm.setStatus(status); - rm.done(); - return status; - } - - // Submit the runnable to plant the breakpoints on dispatch thread. - getExecutor().submit(new Runnable() { - public void run() { - installInitialBreakpoints(breakpointsDmc, initialPlatformBPs, rm); - } - }); - + doBreakpointsRemoved(DebugPlugin.getDefault().getBreakpointManager().getBreakpoints(), dmc, rm); return Status.OK_STATUS; } }.schedule(); } - + /** - * Installs the breakpoints that existed prior to the activation of this - * breakpoints context. + * 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. */ - private void installInitialBreakpoints(final IBreakpointsTargetDMContext dmc, - Map>> initialPlatformBPs, - RequestMonitor rm) - { - // Retrieve the set of platform breakpoints for this context - Map>> platformBPs = fPlatformBPs.get(dmc); - if (platformBPs == null) { - rm.setStatus(new Status(IStatus.ERROR, DsfPlugin.PLUGIN_ID, INVALID_HANDLE, "Invalid context", null)); //$NON-NLS-1$ - rm.done(); - return; - } - - // Install the individual breakpoints on the executor thread - // Requires a counting monitor to know when we're done - final CountingRequestMonitor countingRm = new CountingRequestMonitor(getExecutor(), rm); - countingRm.setDoneCount(initialPlatformBPs.size()); + public ITargetBreakpointInfo[] getTargetBreakpoints(IBreakpointsTargetDMContext dmc, IBreakpoint platformBp) { + Map> platformBPs = fPlatformBPs.get(dmc); - for (final IBreakpoint bp : initialPlatformBPs.keySet()) { - final List> attrs = initialPlatformBPs.get(bp); - // Upon determining the debuggerPath, the breakpoint is installed - installBreakpoint(dmc, bp, attrs, new RequestMonitor(getExecutor(), countingRm)); + if (platformBPs != null) + { + List bpInfo = platformBPs.get(platformBp); + if (bpInfo != null) { + return bpInfo.toArray(new ITargetBreakpointInfo[bpInfo.size()]); + } } + return null; } - - public void stopTrackingBreakpoints(final IBreakpointsTargetDMContext dmc, final RequestMonitor rm) { - // - Remove the target breakpoints for the given execution context - // - Update the maps + /** + * 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) { + 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(); + } + } + } - // Remove the breakpoints for given DMC from the internal maps. - Map>> platformBPs = fPlatformBPs.get(dmc); - if (platformBPs == null) { - rm.setStatus(new Status(IStatus.ERROR, DsfPlugin.PLUGIN_ID, INTERNAL_ERROR, "Breakpoints not installed for given context", null)); //$NON-NLS-1$ - rm.done(); - return; - } - - // Uninstall the individual breakpoints on the executor thread - // Requires a counting monitor to know when we're done - final CountingRequestMonitor countingRm = new CountingRequestMonitor(getExecutor(), rm); - countingRm.setDoneCount(platformBPs.size()); - - for (final IBreakpoint bp : platformBPs.keySet()) { - uninstallBreakpoint(dmc, bp, countingRm); - } + 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 when threads are taken + * can resolve into multiple back-end breakpoints, e.g. when threads are taken * into account. * * @param dmc * @param breakpoint - * @param attrsList + * @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 RequestMonitor rm) + final List> attrsList, final DataRequestMonitor> rm) { // Retrieve the set of breakpoints for this context - final Map>> platformBPs = fPlatformBPs.get(dmc); + final Map> platformBPs = fPlatformBPs.get(dmc); assert platformBPs != null; - final Map> breakpointIDs = fBreakpointDMContexts.get(dmc); - assert breakpointIDs != null; // fBreakpointIds should be updated in parallel with fPlatformBPs + // Ensure the breakpoint is not already installed + assert !platformBPs.containsKey(breakpoint); - // Ensure the breakpoint is not already installed - if (platformBPs.containsKey(breakpoint)) { - rm.setStatus(new Status(IStatus.ERROR, DsfPlugin.PLUGIN_ID, INVALID_STATE, "Breakpoint already installed", null)); //$NON-NLS-1$ - rm.done(); - return; + 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 handleSuccess() { - // Store the platform breakpoint - platformBPs.put(breakpoint, attrsList); - new Job("Breakpoint status update") { //$NON-NLS-1$ - { setSystem(true); } - @Override - protected IStatus run(IProgressMonitor monitor) { - fAttributeTranslator.updateBreakpointStatus(breakpoint, EBreakpointStatusChange.EInstalled); - return Status.OK_STATUS; - }; - }.schedule(); + 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(); } }; @@ -398,26 +446,22 @@ installRM.setDoneCount(attrsList.size()); // Install the back-end breakpoint(s) - for (Map attrs : attrsList) { + for (int _i = 0; _i < attrsList.size(); _i++) { + final int i = _i; fBreakpoints.insertBreakpoint( - dmc, attrs, + dmc, attrsList.get(i), new DataRequestMonitor(getExecutor(), installRM) { @Override protected void handleCompleted() { - List list = breakpointIDs.get(breakpoint); - if (list == null) { - list = new LinkedList(); - breakpointIDs.put(breakpoint, list); - } - + TargetBP targetBP = targetBPsAttempted.get(i); if (isSuccess()) { // Add the breakpoint back-end mapping - list.add(getData()); - } else { - // TODO (bug 219841): need to add breakpoint error status tracking - // in addition to fBreakpointDMContexts. - installRM.setStatus(getStatus()); - } + targetBP.setTargetBreakpoint(getData()); + + targetBPsInstalled.add(targetBP); + } + targetBP.setStatus(getStatus()); + installRM.done(); } }); @@ -430,65 +474,60 @@ * * @param dmc * @param breakpoint - * @param rm + * @param drm */ private void uninstallBreakpoint(final IBreakpointsTargetDMContext dmc, final IBreakpoint breakpoint, - final RequestMonitor rm) + final DataRequestMonitor> drm) { + // Remove the back-end breakpoints + final Map> platformBPs = fPlatformBPs.get(dmc); + if (platformBPs == null) { + drm.setStatus(new Status(IStatus.ERROR, DsfPlugin.PLUGIN_ID, 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 - CountingRequestMonitor removeRM = new CountingRequestMonitor(getExecutor(), rm) { + final CountingRequestMonitor countingRm = new CountingRequestMonitor(getExecutor(), drm) { @Override protected void handleCompleted() { - // Remove the attributes mapping - Map>> platformBPs = fPlatformBPs.get(dmc); - if (platformBPs == null) { - rm.setStatus(new Status(IStatus.ERROR, DsfPlugin.PLUGIN_ID, INVALID_HANDLE, "Invalid context", null)); //$NON-NLS-1$ - rm.done(); - return; - } - platformBPs.remove(breakpoint); + platformBPs.remove(breakpoint); - // Remove the back-end mapping - Map> breakpointIDs = fBreakpointDMContexts.get(dmc); - if (breakpointIDs == null) { - rm.setStatus(new Status(IStatus.ERROR, DsfPlugin.PLUGIN_ID, INVALID_HANDLE, "Invalid breakpoint", null)); //$NON-NLS-1$ - rm.done(); - return; - } - breakpointIDs.get(breakpoint).clear(); - breakpointIDs.remove(breakpoint); - - // Update breakpoint status - new Job("Breakpoint status update") { //$NON-NLS-1$ - { setSystem(true); } - @Override - protected IStatus run(IProgressMonitor monitor) { - fAttributeTranslator.updateBreakpointStatus(breakpoint, EBreakpointStatusChange.EUninstalled); - return Status.OK_STATUS; - }; - }.schedule(); - - rm.done(); + // Complete the request monitor. + drm.setData(bpList); + drm.done(); } }; - // Remove the back-end breakpoints - Map> breakpointIDs = fBreakpointDMContexts.get(dmc); - if (breakpointIDs == null) { - rm.setStatus(new Status(IStatus.ERROR, DsfPlugin.PLUGIN_ID, INVALID_HANDLE, "Invalid breakpoint", null)); //$NON-NLS-1$ - rm.done(); - return; + int count = 0; + for (int i = 0; i < bpList.size(); i++) { + final TargetBP bp = bpList.get(i); + if (bp.getTargetBreakpoint() != null) { + fBreakpoints.removeBreakpoint( + bp.getTargetBreakpoint(), + new RequestMonitor(getExecutor(), countingRm) { + @Override + protected void handleCompleted() { + bp.setStatus(getStatus()); + bp.fAttributesDelta = bp.fAttributes; + if (isSuccess()) { + bp.setTargetBreakpoint(null); + bp.fAttributes = null; + } + countingRm.done(); + } + }); + count++; + } else { + bp.setStatus(Status.OK_STATUS); + } } - - List list = breakpointIDs.get(breakpoint); - int count = 0; - if (list != null) { - for (IBreakpointDMContext bp : list) { - fBreakpoints.removeBreakpoint(bp, removeRM); - } - count = list.size(); - } - removeRM.setDoneCount(count); + countingRm.setDoneCount(count); } /** @@ -497,11 +536,11 @@ * @param context * @param breakpoint * @param attributes - * @param rm + * @param drm * @throws CoreException */ private void modifyBreakpoint(final IBreakpointsTargetDMContext context, final IBreakpoint breakpoint, - final List> newAttrsList0, final IMarkerDelta oldValues, final RequestMonitor rm) + final List> newAttrsList, final DataRequestMonitor> drm) { // This method uses several lists to track the changed breakpoints: // commonAttrsList - attributes which have not changed @@ -513,90 +552,69 @@ // oldAttrsList and newAttrsList // Get the maps - final Map>> platformBPs = fPlatformBPs.get(context); - final Map> breakpointIDs = fBreakpointDMContexts.get(context); - if (platformBPs == null || breakpointIDs == null) { - rm.setStatus(new Status(IStatus.ERROR, DsfPlugin.PLUGIN_ID, INVALID_HANDLE, "Invalid context", null)); //$NON-NLS-1$ - rm.done(); - return; - } - - // Get the original breakpoint attributes - final List> oldAttrsList0 = platformBPs.get(breakpoint); - if (oldAttrsList0 == null) { - rm.setStatus(new Status(IStatus.ERROR, DsfPlugin.PLUGIN_ID, INVALID_HANDLE, "Invalid breakpoint", null)); //$NON-NLS-1$ - rm.done(); - return; - } - - // Get the list of corresponding back-end breakpoints - final List oldBpContexts = new ArrayList(breakpointIDs.get(breakpoint)); - if (oldBpContexts == null) { - rm.setStatus(new Status(IStatus.ERROR, DsfPlugin.PLUGIN_ID, INVALID_HANDLE, "Invalid breakpoint", null)); //$NON-NLS-1$ - rm.done(); + final Map> platformBPs = fPlatformBPs.get(context); + if (platformBPs == null) { + drm.setStatus(new Status(IStatus.ERROR, DsfPlugin.PLUGIN_ID, INVALID_HANDLE, "Invalid context", null)); //$NON-NLS-1$ + drm.done(); return; } - // Calculate the list of attributes maps that have not changed. - // Immediately add these to the list of new breakpoint contexts, - // and remove them from further breakpoint attribute comparisons. - final List> commonAttrsList = getCommonAttributeMaps(newAttrsList0, oldAttrsList0); - final List newBpContexts = new ArrayList(commonAttrsList.size()); - - final List> newAttrsList = new ArrayList>(newAttrsList0); - newAttrsList.removeAll(commonAttrsList); + final List oldBpList = platformBPs.get(breakpoint); + + final List bpList = new ArrayList(newAttrsList.size()); - List> oldAttrsList = new ArrayList>(oldAttrsList0); - for (int i = 0; i < oldAttrsList.size(); i++) { - if (commonAttrsList.contains(oldAttrsList.get(i))) { - if (oldBpContexts.size() > i) { - newBpContexts.add(oldBpContexts.remove(i)); - } - } + if (oldBpList == null || oldBpList.size() == 0) { // not targetBP installed + drm.setData(bpList); + drm.done(); + return; } - oldAttrsList.removeAll(commonAttrsList); - // Create a list of attribute changes. The lenghth of this list will + for (int i = 0; i < newAttrsList.size(); i++) { + bpList.add(new TargetBP(newAttrsList.get(i))); + } + + // Create a list of attribute changes. The length of this list will // always be max(oldAttrList.size(), newAttrsList.size()), padded with // null's if oldAttrsList was longer. - final List> attrDeltasList = getAttributesDeltas(oldAttrsList, newAttrsList); + calcBPsAttrs(oldBpList, bpList); // Create the request monitor that will be called when all // modifying/inserting/removing is complete. - final CountingRequestMonitor countingRM = new CountingRequestMonitor(getExecutor(), rm) { + final CountingRequestMonitor countingRM = new CountingRequestMonitor(getExecutor(), drm) { @Override protected void handleCompleted() { - // Save the new list of breakpoint contexts and attributes - breakpointIDs.put(breakpoint, newBpContexts); - newAttrsList.addAll(commonAttrsList); - platformBPs.put(breakpoint, newAttrsList); - - // Update breakpoint status. updateBreakpointStatus() cannot - // be called on the executor thread, so we need to - // use a Job. - new Job("Breakpoint status update") { //$NON-NLS-1$ - { setSystem(true); } - @Override - protected IStatus run(IProgressMonitor monitor) { - fAttributeTranslator.updateBreakpointStatus(breakpoint, EBreakpointStatusChange.EModified); - return Status.OK_STATUS; - }; - }.schedule(); - - super.handleCompleted(); + // Save the new list of target breakpoints + platformBPs.put(breakpoint, bpList); + drm.setData(bpList); + drm.done(); } }; // Set the count, if could be zero if no breakpoints have actually changed. - countingRM.setDoneCount(attrDeltasList.size()); + int coutingRmCount = 0; // Process the changed breakpoints. - for (int i = 0; i < attrDeltasList.size(); i++) { - if (attrDeltasList.get(i) == null) { + for (int _i = 0; _i < bpList.size(); _i++) { + final int i = _i; + final TargetBP bp = bpList.get(i); + if (bp.fAttributes == null) { // The list of new attribute maps was shorter than the old. - // Remove the corresponding target-side bp. - fBreakpoints.removeBreakpoint(oldBpContexts.get(i), countingRM); - } else if ( i >= oldBpContexts.size()) { + // Remove the corresponding target-side bp. + // Note the target BP context may be null if the target + // BP failed to insert in the first place. + if (bp.getTargetBreakpoint() != null) { + fBreakpoints.removeBreakpoint( + bp.getTargetBreakpoint(), + new RequestMonitor(getExecutor(), countingRM) { + @Override + protected void handleCompleted() { + bp.fStatus = getStatus(); + countingRM.done(); + } + }); + coutingRmCount++; + } + } else if ( bp.getTargetBreakpoint() == null) { // The list of new attribute maps was longer, just insert // the new breakpoint final Map attrs = newAttrsList.get(i); @@ -604,66 +622,68 @@ context, attrs, new DataRequestMonitor(getExecutor(), countingRM) { @Override - protected void handleSuccess() { - newBpContexts.add(getData()); + protected void handleCompleted() { + if (isSuccess()) { + bp.fTargetBPContext = getData(); + } + bp.fStatus = getStatus(); countingRM.done(); } }); - } else if ( !fAttributeTranslator.canUpdateAttributes(oldBpContexts.get(i), attrDeltasList.get(i)) ) { + coutingRmCount++; + } else if (bp.fAttributesDelta.size() == 0) { + // Breakpoint attributes have not changed, only copy over the old status. + bp.fStatus = oldBpList.get(i).fStatus; + } else if ( !fAttributeTranslator.canUpdateAttributes(bp.getTargetBreakpoint(), bp.fAttributesDelta) ) { // The attribute translator tells us that the debugger cannot modify the // breakpoint to change the given attributes. Remove the breakpoint // and insert a new one. - final Map attrs = newAttrsList.get(i); - fBreakpoints.removeBreakpoint( - oldBpContexts.get(i), + RequestMonitor removeRm = new RequestMonitor(getExecutor(), countingRM) { + @Override + protected void handleCompleted() { + if (isSuccess()) { + bp.fTargetBPContext = null; + fBreakpoints.insertBreakpoint( + context, newAttrsList.get(i), + new DataRequestMonitor(getExecutor(), countingRM) { + @Override + protected void handleCompleted() { + if (isSuccess()) { + bp.fTargetBPContext = getData(); + } + bp.fStatus = getStatus(); + countingRM.done(); + } + }); + } else { + // Failed to remove old breakpoint, do not proceed to insert a new one + // just save the error from target with the old context. + bp.fStatus = getStatus(); + countingRM.done(); + } + } + }; + + fBreakpoints.removeBreakpoint(bp.getTargetBreakpoint(), removeRm); + coutingRmCount++; + } else { + // The back end can modify the breakpoint. Update the breakpoint with the + // new attributes. + fBreakpoints.updateBreakpoint( + bp.getTargetBreakpoint(), bp.fAttributes, new RequestMonitor(getExecutor(), countingRM) { @Override protected void handleCompleted() { - fBreakpoints.insertBreakpoint( - context, attrs, - new DataRequestMonitor(getExecutor(), countingRM) { - @Override - protected void handleCompleted() { - if (isSuccess()) { - newBpContexts.add(getData()); - } else { - // TODO (bug 219841): need to add breakpoint error status tracking - // in addition to fBreakpointDMContexts. - } - countingRM.done(); - } - }); - } - }); - } else { - // The back end can modify the breakpoint. Update the breakpoint with the - // new attributes. - final IBreakpointDMContext bpCtx = oldBpContexts.get(i); - fBreakpoints.updateBreakpoint( - oldBpContexts.get(i), newAttrsList.get(i), - new RequestMonitor(getExecutor(), countingRM) { - @Override - protected void handleSuccess() { - newBpContexts.add(bpCtx); + bp.fStatus = getStatus(); countingRM.done(); } }); + coutingRmCount++; } } + countingRM.setDoneCount(coutingRmCount); } - private List> getCommonAttributeMaps(List> array1, List> array2) - { - List> intersection = new LinkedList>(); - List> list2 = new ArrayList>(array2); - for (Map array1Map : array1) { - if (list2.remove(array1Map)) { - intersection.add(array1Map); - } - } - return intersection; - } - /** * Determine the set of modified attributes * @@ -671,53 +691,45 @@ * @param newAttributes * @return */ - private List> getAttributesDeltas(List> oldAttributesList, List> newAttributesList) { - List> deltas = new ArrayList>(oldAttributesList.size()); - + private void calcBPsAttrs(List oldBpList, List bpList) { // Go through the bp attributes common to the old and the new lists and calculate // their deltas. - for (int i = 0; i < oldAttributesList.size() && i < newAttributesList.size(); i++) { - Map oldAttributes = oldAttributesList.get(i); - Map newAttributes = newAttributesList.get(i); + int i = 0; + for (i = 0; i < oldBpList.size() && i < bpList.size(); i++) { + TargetBP newBp = bpList.get(i); + TargetBP oldBp = oldBpList.get(i); + newBp.fTargetBPContext = oldBp.getTargetBreakpoint(); + Map oldAttributes = oldBp.fAttributes; + Map newAttributes = newBp.fAttributes; - 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); - } - deltas.add(delta); + if (oldAttributes == null) { + // Reached a point in the old BP list where breakpoints were + // removed. Break out of the loop. + break; + } + + bpList.get(i).fAttributesDelta = getAttributesDelta(oldAttributes, newAttributes); } // Add all the new attributes as deltas - for (int i = deltas.size(); i < newAttributesList.size(); i++) { - deltas.add(newAttributesList.get(i)); + for (; i < bpList.size(); i++) { + TargetBP newBP = bpList.get(i); + newBP.fAttributesDelta = newBP.fAttributes; } - // For any old attribute Maps that were removed, insert a null value in the deltas list. - for (int i = deltas.size(); i < oldAttributesList.size(); i++) { - deltas.add(null); - } - - return deltas; + // For breakpoints that were removed create TargetBP entry with a + // null set of attributes + for (; i < oldBpList.size(); i++) { + TargetBP oldBp = oldBpList.get(i); + if (oldBp.fAttributes == null) { + // Guard against old removed breakpoints + break; + } + TargetBP newBp = new TargetBP(null); + newBp.fTargetBPContext = oldBp.getTargetBreakpoint(); + newBp.fAttributesDelta = oldBpList.get(i).fAttributes; + bpList.add(newBp); + } } /////////////////////////////////////////////////////////////////////////// @@ -725,146 +737,612 @@ /////////////////////////////////////////////////////////////////////////// public void breakpointManagerEnablementChanged(boolean enabled) { - for (IBreakpoint breakpoint : fBreakpointManager.getBreakpoints()) { - breakpointChanged(breakpoint, null); + Map platformAttrDelta = new HashMap(1); + platformAttrDelta.put(IBreakpoint.ENABLED, enabled); + + Map> bp2DeltaMap = new HashMap>(); + for (IBreakpoint bp : fBreakpointManager.getBreakpoints()) { + if (! fAttributeTranslator.supportsBreakpoint(bp)) + continue; + + bp2DeltaMap.put(bp, platformAttrDelta); } + + doBreakpointsChanged(bp2DeltaMap); } @ThreadSafe - public void breakpointAdded(final IBreakpoint breakpoint) { - if (fAttributeTranslator.supportsBreakpoint(breakpoint)) { - try { - // Retrieve the breakpoint attributes - final List> attrsArray = - fAttributeTranslator.getBreakpointAttributes(breakpoint, fBreakpointManager.isEnabled()); + public void breakpointsAdded(final IBreakpoint[] bps) { + doBreakpointsAdded(bps, null, null); + } + + @SuppressWarnings("unchecked") + private void doBreakpointsAdded(final IBreakpoint[] bps, final IBreakpointsTargetDMContext bpsTargetDmc, final RequestMonitor rm) { + final List bpCandidates = new ArrayList(bps.length); + + for (int i = 0; i < bps.length; i++) { + IBreakpoint bp = bps[i]; + + if (fAttributeTranslator.supportsBreakpoint(bp)) { + try { + if (fAttributeTranslator2 != null) { + if (bp.getMarker() == null) + continue; + + // if the breakpoint is not enabled, ask translator2 if it can set (and manage) + // disabled breakpoint itself. If not, just bail out. + // + Map platformAttrs = bp.getMarker().getAttributes(); + + if (! (Boolean)platformAttrs.get(IBreakpoint.ENABLED) || ! fBreakpointManager.isEnabled()) { + Map platformAttr = new HashMap(1); + platformAttr.put(IBreakpoint.ENABLED, Boolean.FALSE); + Map targetAttr = fAttributeTranslator2.convertAttributeDelta(platformAttr); + if (! fAttributeTranslator2.canUpdateAttributes(null, targetAttr)) { + // bail out. + continue; + } + } + } + + bpCandidates.add(bp); + } catch (CoreException e) { + DsfPlugin.getDefault().getLog().log(e.getStatus()); + } + } + } + + // Nothing to do + if (bpCandidates.isEmpty()) { + if (rm != null) { + rm.done(); + } + return; + } + + try { + getExecutor().execute(new DsfRunnable() { + public void run() { + final Map> eventBPs = + new HashMap>(bpCandidates.size(), 1); + + CountingRequestMonitor processPendingCountingRm = new CountingRequestMonitor(getExecutor(), rm) { + @Override + protected void handleCompleted() { + processPendingRequests(); + fireUpdateBreakpointsStatus(eventBPs, BreakpointEventType.ADDED); + super.handleCompleted(); + } + }; + int processPendingCountingRmCount = 0; + + for (final IBreakpoint breakpoint : bpCandidates) { + final Map targetBPs = + new HashMap(fPlatformBPs.size(), 1); + eventBPs.put(breakpoint, targetBPs); + + if (fRunningEvents.contains(breakpoint)) { + PendingEventInfo pendingEvent = new PendingEventInfo(BreakpointEventType.ADDED, bpsTargetDmc, processPendingCountingRm); + processPendingCountingRmCount++; + updatePendingRequest(breakpoint, pendingEvent); + continue; + } + // Mark the breakpoint as being updated and go + fRunningEvents.add(breakpoint); - getExecutor().execute(new DsfRunnable() { - public void run() { - //TODO pp: need to track pending requests - - final CountingRequestMonitor countingRm = new CountingRequestMonitor(getExecutor(), null) { - @Override - protected void handleError() { - if (getStatus().getSeverity() == IStatus.ERROR) { - DsfPlugin.getDefault().getLog().log(getStatus()); + final CountingRequestMonitor bpTargetsCountingRm = new CountingRequestMonitor(getExecutor(), processPendingCountingRm) { + @Override + protected void handleCompleted() { + // Indicate that the running event has completed + fRunningEvents.remove(breakpoint); + super.handleCompleted(); } - } - }; - countingRm.setDoneCount(fPlatformBPs.size()); - + }; + int bpTargetsCountingRmCount = 0; + processPendingCountingRmCount++; + + // Install the breakpoint in all the execution contexts for (final IBreakpointsTargetDMContext dmc : fPlatformBPs.keySet()) { - installBreakpoint(dmc, breakpoint, attrsArray, new RequestMonitor(getExecutor(), countingRm)); + if (bpsTargetDmc != null && !bpsTargetDmc.equals(dmc)) { + continue; + } + + // Now ask lower level to set the bp. + // + if (fAttributeTranslator2 != null) { + fAttributeTranslator2.getTargetBreakpointAttributes(dmc, breakpoint, fBreakpointManager.isEnabled(), + new DataRequestMonitor>>(getExecutor(), bpTargetsCountingRm){ + @Override + protected void handleSuccess() { + installBreakpoint( + dmc, breakpoint, getData(), + new DataRequestMonitor>(getExecutor(), bpTargetsCountingRm) { + @Override + protected void handleSuccess() { + targetBPs.put(dmc, getData().toArray(new ITargetBreakpointInfo[getData().size()])); + super.handleSuccess(); + }; + }); + }}); + } + else { // Old way + List> attrsArray; + try { + attrsArray = fAttributeTranslator.getBreakpointAttributes(breakpoint, fBreakpointManager.isEnabled()); + } catch (CoreException e) { + attrsArray = new ArrayList>(); + DsfPlugin.getDefault().getLog().log(e.getStatus()); + } + + installBreakpoint( + dmc, breakpoint, attrsArray, + new DataRequestMonitor>(getExecutor(), bpTargetsCountingRm) { + @Override + protected void handleSuccess() { + targetBPs.put(dmc, getData().toArray(new ITargetBreakpointInfo[getData().size()])); + super.handleSuccess(); + }; + }); + } + + bpTargetsCountingRmCount++; } - } - }); - } catch (CoreException e) { - DsfPlugin.getDefault().getLog().log(e.getStatus()); - } catch (RejectedExecutionException e) { + bpTargetsCountingRm.setDoneCount(bpTargetsCountingRmCount); + } + processPendingCountingRm.setDoneCount(processPendingCountingRmCount); + } + }); + } catch (RejectedExecutionException e) { + IStatus status = new Status(IStatus.ERROR, DsfPlugin.PLUGIN_ID, 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 { + DsfPlugin.getDefault().getLog().log(status); } } - } /////////////////////////////////////////////////////////////////////////// // IBreakpointListener implementation /////////////////////////////////////////////////////////////////////////// - public void breakpointChanged(final IBreakpoint breakpoint, final IMarkerDelta delta) { - if (fAttributeTranslator.supportsBreakpoint(breakpoint)) { - try { - // Retrieve the breakpoint attributes - final List> attrsArray = - fAttributeTranslator.getBreakpointAttributes(breakpoint, fBreakpointManager.isEnabled()); - - // Modify the breakpoint in all the target contexts - getExecutor().execute( new DsfRunnable() { - public void run() { + @SuppressWarnings("unchecked") + public void breakpointsChanged(IBreakpoint[] bps, IMarkerDelta[] deltas) { + Map> bp2DeltaMap = new HashMap>(); + for (int i = 0; i < bps.length; i++) { + IBreakpoint bp = bps[i]; + + if (deltas[i] == null) + continue; + + if (bp.getMarker() == null) + continue; + + if (! fAttributeTranslator.supportsBreakpoint(bp)) + continue; - // If the breakpoint is currently being updated, queue the request and exit - if (fPendingRequests.contains(breakpoint)) { - fPendingBreakpoints.add(breakpoint); - return; - } + try { + Map oldAttrs = deltas[i].getAttributes(); + Map newAttrs = bp.getMarker().getAttributes(); + + Map platformAttrDelta = getAttributesDelta(oldAttrs, newAttrs); + + if (platformAttrDelta.size() == 0) // no change. possible when user cancels breakpoint properties dialog. + continue; - // Keep track of the updates - final CountingRequestMonitor countingRm = new CountingRequestMonitor(getExecutor(), null) { - @Override - protected void handleCompleted() { + bp2DeltaMap.put(bp, platformAttrDelta); + } catch (CoreException e) { + DsfPlugin.getDefault().getLog().log(e.getStatus()); + } + } + + doBreakpointsChanged(bp2DeltaMap); + } + + /** + * + * @param bp2DeltaMap - pairs of (breakpoint, attrDelta), where attrDelta contains changed + * and new attributes for the breakpoint. + */ + private void doBreakpointsChanged(Map> bp2DeltaMap) { + + final Map>> bpsAttrs = + new HashMap>>(bp2DeltaMap.size() * 4/3); - if (!isSuccess()) { - if (getStatus().getSeverity() == IStatus.ERROR) { - DsfPlugin.getDefault().getLog().log(getStatus()); - } - } - - // Indicate that the pending request has completed - fPendingRequests.remove(breakpoint); + for (IBreakpoint bp : bp2DeltaMap.keySet()) { + try { + Map platformAttrDelta = bp2DeltaMap.get(bp); + + if (fAttributeTranslator2 != null) { + Map targetAttrDelta = fAttributeTranslator2.convertAttributeDelta(platformAttrDelta); + + if (! fAttributeTranslator2.canUpdateAttributes(null, targetAttrDelta)) { + // DSF client cannot handle at least one of the attribute change, just remove + // old target BPs and install new ones. + final IBreakpoint[] platformBPs = new IBreakpoint[] {bp}; + + if (platformAttrDelta.containsKey(IBreakpoint.ENABLED)) { + if ((Boolean)platformAttrDelta.get(IBreakpoint.ENABLED)) + // platform BP changed from disabled to enabled + doBreakpointsAdded(platformBPs, null, null); + else + doBreakpointsRemoved(platformBPs, null, null); + } + else { + // other attribute change, remove old and install new. + doBreakpointsRemoved(platformBPs, null, new RequestMonitor(getExecutor(), null) { + @Override + protected void handleSuccess() { + doBreakpointsAdded(platformBPs, null, null); + }}); + } + } + else + updateBreakpoint(bp, targetAttrDelta); + } + else { // old way + + // Retrieve the breakpoint attributes + List> attrsArray = + fAttributeTranslator.getBreakpointAttributes(bp, fBreakpointManager.isEnabled()); + + bpsAttrs.put(bp, attrsArray); + } + } catch (CoreException e) { + DsfPlugin.getDefault().getLog().log(e.getStatus()); + } + } + + if (bpsAttrs.isEmpty()) return; // nothing to do + + try { + // Modify the breakpoint in all the target contexts + getExecutor().execute( new DsfRunnable() { + public void run() { + final Map> eventBPs = + new HashMap>(bpsAttrs.size(), 1); - // Process the next pending update for this breakpoint - if (fPendingBreakpoints.contains(breakpoint)) { - fPendingBreakpoints.remove(breakpoint); - new Job("Deferred breakpoint changed job") { //$NON-NLS-1$ - { setSystem(true); } - @Override - protected IStatus run(IProgressMonitor monitor) { - breakpointChanged(breakpoint, delta); - return Status.OK_STATUS; - }; - }.schedule(); - } - } - }; - countingRm.setDoneCount(fPlatformBPs.size()); + CountingRequestMonitor processPendingCountingRm = new CountingRequestMonitor(getExecutor(), null) { + @Override + protected void handleCompleted() { + processPendingRequests(); + fireUpdateBreakpointsStatus(eventBPs, BreakpointEventType.MODIFIED); + } + }; + int processPendingCountingRmCount = 0; + + for (final IBreakpoint breakpoint : bpsAttrs.keySet()) { + final Map targetBPs = + new HashMap(fPlatformBPs.size(), 1); + eventBPs.put(breakpoint, targetBPs); + // If the breakpoint is currently being updated, queue the request and exit + if (fRunningEvents.contains(breakpoint)) { + updatePendingRequest(breakpoint, new PendingEventInfo(BreakpointEventType.MODIFIED, null, null)); + continue; + } + + // Keep track of the updates + CountingRequestMonitor bpTargetsCountingRm = + new CountingRequestMonitor(getExecutor(), processPendingCountingRm) { + @Override + protected void handleCompleted() { + // Indicate that the running event has completed + fRunningEvents.remove(breakpoint); + super.handleCompleted(); + } + }; + processPendingCountingRmCount++; + bpTargetsCountingRm.setDoneCount(fPlatformBPs.size()); + // Mark the breakpoint as being updated and go - fPendingRequests.add(breakpoint); + fRunningEvents.add(breakpoint); // Modify the breakpoint in all the execution contexts for (final IBreakpointsTargetDMContext dmc : fPlatformBPs.keySet()) { - modifyBreakpoint(dmc, breakpoint, attrsArray, delta, new RequestMonitor(getExecutor(), countingRm)); + modifyBreakpoint( + dmc, breakpoint, bpsAttrs.get(breakpoint), + new DataRequestMonitor>(getExecutor(), bpTargetsCountingRm) { + @Override + protected void handleSuccess() { + targetBPs.put(dmc, getData().toArray(new ITargetBreakpointInfo[getData().size()])); + super.handleSuccess(); + }; + }); } - } - }); - } catch (CoreException e) { - DsfPlugin.getDefault().getLog().log(e.getStatus()); - } catch (RejectedExecutionException e) { - } - } + } + processPendingCountingRm.setDoneCount(processPendingCountingRmCount); + } + }); + } catch (RejectedExecutionException e) { + DsfPlugin.getDefault().getLog().log(new Status(IStatus.ERROR, DsfPlugin.PLUGIN_ID, 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, update all its target BPs with the given attribute change. + * + * @param bp + * @param targetAttrDelta - target attribute change. + */ + private void updateBreakpoint(final IBreakpoint bp, final Map targetAttrDelta) { + getExecutor().execute(new DsfRunnable() { + public void run() { + for (IBreakpointsTargetDMContext dmc : fPlatformBPs.keySet()) { + List targetBPs = fPlatformBPs.get(dmc).get(bp); + if (targetBPs != null) { + for (TargetBP tbp : targetBPs) { + // this must be an installed bp. + assert (tbp.getTargetBreakpoint() != null); + + fBreakpoints.updateBreakpoint(tbp.getTargetBreakpoint(), targetAttrDelta, new RequestMonitor(getExecutor(), null) {}); + } + } + } + }}); } - public void breakpointRemoved(final IBreakpoint breakpoint, IMarkerDelta delta) { + public void breakpointsRemoved(final IBreakpoint[] bps, IMarkerDelta delta[]) { + doBreakpointsRemoved(bps, null, null); + } + + public 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 (fAttributeTranslator.supportsBreakpoint(bp)) { + if (bpsTargetDmc == null) + bpCandidates.add(bp); + else if (fPlatformBPs.get(bpsTargetDmc).containsKey(bp)) // target BPs are installed in the context + bpCandidates.add(bp); + } + } + + if (bpCandidates.isEmpty()) { // nothing to do + if (rm != null) + rm.done(); + return; + } + + try { + getExecutor().execute(new DsfRunnable() { + public void run() { + final Map> eventBPs = + new HashMap>(bpCandidates.size(), 1); - if (fAttributeTranslator.supportsBreakpoint(breakpoint)) { - try { - getExecutor().execute(new DsfRunnable() { - public void run() { - //TODO pp: need to track pending requests + CountingRequestMonitor processPendingCountingRm = new CountingRequestMonitor(getExecutor(), rm) { + @Override + protected void handleCompleted() { + processPendingRequests(); + fireUpdateBreakpointsStatus(eventBPs, BreakpointEventType.REMOVED); + super.handleCompleted(); + } + }; + int processPendingCountingRmCount = 0; + + for (final IBreakpoint breakpoint : bpCandidates) { + final Map targetBPs = + new HashMap(fPlatformBPs.size(), 1); + eventBPs.put(breakpoint, targetBPs); + + // If the breakpoint is currently being updated, queue the request and exit + if (fRunningEvents.contains(breakpoint)) { + PendingEventInfo pendingEvent = new PendingEventInfo(BreakpointEventType.REMOVED, bpsTargetDmc, processPendingCountingRm); + processPendingCountingRmCount++; + updatePendingRequest(breakpoint, pendingEvent); + continue; + } + + CountingRequestMonitor bpTargetDmcCRM = new CountingRequestMonitor(getExecutor(), processPendingCountingRm) { + @Override + protected void handleCompleted() { + // Indicate that the running event has completed + fRunningEvents.remove(breakpoint); + super.handleCompleted(); + } + }; + processPendingCountingRmCount++; + int bpTargetDmcCRMCount = 0; - CountingRequestMonitor countingRm = new CountingRequestMonitor(getExecutor(), null) { - @Override - protected void handleError() { - if (getStatus().getSeverity() == IStatus.ERROR) { - DsfPlugin.getDefault().getLog().log(getStatus()); - } - } - }; - countingRm.setDoneCount(fPlatformBPs.size()); + // Remove the breakpoint in all the execution contexts + for (final IBreakpointsTargetDMContext dmc : fPlatformBPs.keySet()) { + if (bpsTargetDmc != null && !bpsTargetDmc.equals(dmc)) { + continue; + } + + if (fPlatformBPs.get(dmc).containsKey(breakpoint)) { // there are targetBPs installed + uninstallBreakpoint( + dmc, breakpoint, + new DataRequestMonitor>(getExecutor(), bpTargetDmcCRM) { + @Override + protected void handleSuccess() { + targetBPs.put(dmc, getData().toArray(new ITargetBreakpointInfo[getData().size()])); + super.handleSuccess(); + }; + }); + bpTargetDmcCRMCount++; + } else { + // Breakpoint not installed for given context, do nothing. + } + } + bpTargetDmcCRM.setDoneCount(bpTargetDmcCRMCount); + } + + processPendingCountingRm.setDoneCount(processPendingCountingRmCount); + } + }); + } catch (RejectedExecutionException e) { + IStatus status = new Status(IStatus.ERROR, DsfPlugin.PLUGIN_ID, 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 { + DsfPlugin.getDefault().getLog().log(status); + } + } + } + + 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() { + if (fPendingEvents.isEmpty()) return; // Nothing to do + + final List modifyBPs = new ArrayList(1); + final Map> addBPs = + new HashMap>(1); + final Map> removeBPs = + new HashMap>(1); + + // Make a copy to avoid java.util.ConcurrentModificationException. + Set bpsInPendingEvents = new HashSet(fPendingEvents.keySet()); +// for (IBreakpoint bp : fPendingEvents.keySet()) { + for (IBreakpoint bp : bpsInPendingEvents) { + // Process the next pending update for this breakpoint + if (!fRunningEvents.contains(bp)) { + LinkedList eventInfoList = fPendingEvents.get(bp); + PendingEventInfo eventInfo = eventInfoList.removeFirst(); + if (eventInfoList.isEmpty()) { + fPendingEvents.remove(bp); + } + BreakpointEventType type = eventInfo.fEventType; + if (type.equals(BreakpointEventType.MODIFIED)) { + modifyBPs.add(eventInfo); + } else if (type.equals(BreakpointEventType.ADDED)){ + List addList = addBPs.get(eventInfo.fBPsTargetDmc); + if (addList == null) { + addList = new ArrayList(1); + addBPs.put(eventInfo.fBPsTargetDmc, addList); + } + addList.add(eventInfo); + } else if (type.equals(BreakpointEventType.REMOVED)){ + List removeList = removeBPs.get(eventInfo.fBPsTargetDmc); + if (removeList == null) { + removeList = new ArrayList(1); + removeBPs.put(eventInfo.fBPsTargetDmc, removeList); + } + removeList.add(eventInfo); + } + } + } - // Remove the breakpoint in all the execution contexts - for (IBreakpointsTargetDMContext dmc : fPlatformBPs.keySet()) { - if (fPlatformBPs.get(dmc).remove(breakpoint) != null) { - uninstallBreakpoint(dmc, breakpoint, countingRm); - } else { - // Breakpoint not installed for given context, do nothing. - } - } - } - }); - } catch (RejectedExecutionException e) { - } - } + if (modifyBPs.size() != 0 || removeBPs.size() != 0) { + new Job("Deferred breakpoint changed job") { //$NON-NLS-1$ + { setSystem(true); } + @Override + protected IStatus run(IProgressMonitor monitor) { + if (modifyBPs.size() != 0) { + breakpointsChanged(modifyBPs.toArray(new IBreakpoint[modifyBPs.size()]), null); + } + if (addBPs.size() != 0) { + for (Map.Entry> addBPsEntry : addBPs.entrySet()) { + IBreakpointsTargetDMContext bpsTargetDmc = addBPsEntry.getKey(); + final List addBpList = addBPsEntry.getValue(); + RequestMonitor rm = new RequestMonitor(ImmediateExecutor.getInstance(), null) { + @Override + protected void handleCompleted() { + for (PendingEventInfo eventInfo : addBpList) { + if (eventInfo.fRequestMonitor != null) { + eventInfo.fRequestMonitor.done(); + } + } + }; + }; + doBreakpointsAdded(addBpList.toArray(new IBreakpoint[addBpList.size()]), bpsTargetDmc, rm); + } + } + if (removeBPs.size() != 0) { + for (Map.Entry> removeBPsEntry : removeBPs.entrySet()) { + IBreakpointsTargetDMContext bpsTargetDmc = removeBPsEntry.getKey(); + final List removeBpList = removeBPsEntry.getValue(); + RequestMonitor rm = new RequestMonitor(ImmediateExecutor.getInstance(), null) { + @Override + protected void handleCompleted() { + for (PendingEventInfo eventInfo : removeBpList) { + if (eventInfo.fRequestMonitor != null) { + eventInfo.fRequestMonitor.done(); + } + } + }; + }; + doBreakpointsRemoved(removeBpList.toArray(new IBreakpoint[removeBpList.size()]), bpsTargetDmc, rm); + } + } + return Status.OK_STATUS; + }; + }.schedule(); + } + } + + 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) { + for (IBreakpoint bp : eventBPs.keySet()) { + fAttributeTranslator.updateBreakpointStatus(bp); + } + + if (fAttributeTranslator2 != null) { + 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 -r c2563c416525 -r 6b1088abccf8 cdt/cdt_6_0_x/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IBreakpointAttributeTranslator.java --- a/cdt/cdt_6_0_x/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IBreakpointAttributeTranslator.java Tue Nov 10 09:58:50 2009 -0600 +++ b/cdt/cdt_6_0_x/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IBreakpointAttributeTranslator.java Wed Nov 11 14:48:30 2009 -0600 @@ -33,13 +33,17 @@ public List> getBreakpointAttributes(IBreakpoint breakpoint, boolean bpManagerEnabled) throws CoreException; + /** + * Whether DSF client can handle the given attribute change itself. If not, DSF will + * try to reinstall the breakpoint to apply the change. + * + * @param bp - can be null. + * @param delta + * @return + */ public boolean canUpdateAttributes(IBreakpointDMContext bp, Map delta); public boolean supportsBreakpoint(IBreakpoint bp); - enum EBreakpointStatusChange { - EInstalled, EUninstalled, EModified - } - - public void updateBreakpointStatus(IBreakpoint bp, EBreakpointStatusChange change); + public void updateBreakpointStatus(IBreakpoint bp); } \ No newline at end of file diff -r c2563c416525 -r 6b1088abccf8 cdt/cdt_6_0_x/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IBreakpointAttributeTranslatorExtension.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cdt/cdt_6_0_x/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IBreakpointAttributeTranslatorExtension.java Wed Nov 11 14:48:30 2009 -0600 @@ -0,0 +1,80 @@ +/******************************************************************************* + * 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 + *******************************************************************************/ +package org.eclipse.cdt.dsf.debug.service; + +import java.util.List; +import java.util.Map; + +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.ThreadSafeAndProhibitedFromDsfExecutor; +import org.eclipse.cdt.dsf.debug.service.BreakpointsMediator.BreakpointEventType; +import org.eclipse.cdt.dsf.debug.service.BreakpointsMediator.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 interface + * + * @since 2.1 + */ + +@ThreadSafeAndProhibitedFromDsfExecutor("") +public interface IBreakpointAttributeTranslatorExtension extends IBreakpointAttributeTranslator { + /** + * Convert all attributes of the given platform breakpoint (BP) to + * attributes of potential target breakpoints. Two tasks are involved:
+ * 1. Convert the attributes to debugger specific ones, if needed. For + * instance, GDB implementation has its own breakpoint attribute keys.
+ * 2. Resolve the breakpoint. A platform BP may be mapped to two 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 return an + * attribute map for each of the target BP.
+ *
+ * This method 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. + * @param breakpoint + * - platform breakpoint. + * @param bpManagerEnabled + * - flag from platform breakpoint manager indicating that all + * breakpoints are enabled. + * @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 + */ + public void getTargetBreakpointAttributes(IBreakpointsTargetDMContext context, IBreakpoint breakpoint, + boolean bpManagerEnabled, DataRequestMonitor>> drm); + + /** + * Convert platform breakpoint attributes to target-recognizable attributes. + * This method does not perform task #2 done by {@link this#getTargetBreakpointAttributes(List, IBreakpoint, boolean)}. + * + * @param platformBPAttrDelta + * @return + */ + public Map convertAttributeDelta(Map platformBPAttrDelta); + + /** + * Update platform about breakpoint status change, e.g. breakpoint installed on target successfully or breakpoint + * removed from target successfully. + * + * @param bpsInfo + * @param eventType + */ + public void updateBreakpointsStatus(Map> bpsInfo, BreakpointEventType eventType); +} \ No newline at end of file