From 293998da18c4c15613b9a6880c351e5ce1cb6c71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torbj=C3=B6rn=20Svensson?= Date: Fri, 20 Nov 2020 07:40:33 +0100 Subject: [PATCH] Bug 568228: Add a way for DSF Data Model to initiate refresh all MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is no way to predict what the user might do during for example the launch sequence, so as a last resort, tell the UI to drop all caches and refresh the data as the last step of the launch sequence. Change-Id: I97731c8286657a0fc1111ba41deb47863181a453 Also-by: Jonah Graham Signed-off-by: Torbjörn Svensson --- .../META-INF/MANIFEST.MF | 2 +- .../mi/service/command/AbstractMIControl.java | 33 ++++++++++ .../ui/viewmodel/AbstractDebugVMAdapter.java | 40 +++++++++++ .../.settings/.api_filters | 8 +++ dsf/org.eclipse.cdt.dsf/META-INF/MANIFEST.MF | 2 +- .../command/ICommandControlService.java | 18 +++++ .../cdt/dsf/service/AbstractDsfService.java | 8 +++ .../cdt/dsf/service/DsfServicesTracker.java | 66 ++++++++++++++++++- .../core/GDBJtagDSFFinalLaunchSequence.java | 3 +- 9 files changed, 176 insertions(+), 4 deletions(-) diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/META-INF/MANIFEST.MF b/dsf-gdb/org.eclipse.cdt.dsf.gdb/META-INF/MANIFEST.MF index 8b3281d8c1a..b96d89727bb 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/META-INF/MANIFEST.MF +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/META-INF/MANIFEST.MF @@ -3,7 +3,7 @@ Bundle-ManifestVersion: 2 Bundle-Name: %pluginName Bundle-Vendor: %providerName Bundle-SymbolicName: org.eclipse.cdt.dsf.gdb;singleton:=true -Bundle-Version: 6.0.100.qualifier +Bundle-Version: 6.1.0.qualifier Bundle-Activator: org.eclipse.cdt.dsf.gdb.internal.GdbPlugin Bundle-Localization: plugin Require-Bundle: org.eclipse.core.runtime, diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/AbstractMIControl.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/AbstractMIControl.java index d94e9dd2901..4908a50a763 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/AbstractMIControl.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/AbstractMIControl.java @@ -29,9 +29,11 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.RejectedExecutionException; @@ -39,8 +41,11 @@ import java.util.concurrent.RejectedExecutionException; import org.eclipse.cdt.dsf.concurrent.ConfinedToDsfExecutor; import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; import org.eclipse.cdt.dsf.concurrent.DsfRunnable; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.datamodel.AbstractDMEvent; import org.eclipse.cdt.dsf.datamodel.DMContexts; import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.debug.service.ICachingService; import org.eclipse.cdt.dsf.debug.service.IRunControl; import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMContext; import org.eclipse.cdt.dsf.debug.service.command.ICommand; @@ -68,6 +73,7 @@ import org.eclipse.cdt.dsf.mi.service.command.output.MIResultRecord; import org.eclipse.cdt.dsf.mi.service.command.output.MIStreamRecord; import org.eclipse.cdt.dsf.mi.service.command.output.MIValue; import org.eclipse.cdt.dsf.service.AbstractDsfService; +import org.eclipse.cdt.dsf.service.DsfServicesTracker; import org.eclipse.cdt.dsf.service.DsfSession; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; @@ -145,6 +151,16 @@ public abstract class AbstractMIControl extends AbstractDsfService implements IM private CommandFactory fCommandFactory; + /** + * Event indicating that the back end process has started. + */ + private static class RefreshAllDMEvent extends AbstractDMEvent + implements ICommandControlRefreshAllDMEvent { + public RefreshAllDMEvent(ICommandControlDMContext context) { + super(context); + } + } + public AbstractMIControl(DsfSession session) { this(session, false, false, new CommandFactory()); } @@ -1226,4 +1242,21 @@ public abstract class AbstractMIControl extends AbstractDsfService implements IM processCommandDone(commandHandle, info); } } + + /** + * @since 6.1 + */ + @Override + public void flushAllCachesAndRefresh(RequestMonitor rm) { + DsfServicesTracker servicesTracker = getServicesTracker(); + + Set services = new HashSet<>(servicesTracker.getServices(ICachingService.class)); + for (ICachingService service : services) { + service.flushCache(null); + } + + // Issue a refresh event for any services or UI that is not an ICachingService + getSession().dispatchEvent(new RefreshAllDMEvent(getContext()), getProperties()); + rm.done(); + } } diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/AbstractDebugVMAdapter.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/AbstractDebugVMAdapter.java index 10a4b7eb6ed..352d921ccee 100644 --- a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/AbstractDebugVMAdapter.java +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/AbstractDebugVMAdapter.java @@ -18,11 +18,21 @@ import java.util.concurrent.RejectedExecutionException; import org.eclipse.cdt.dsf.concurrent.DsfRunnable; import org.eclipse.cdt.dsf.debug.service.IRunControl; import org.eclipse.cdt.dsf.debug.service.IRunControl.ISuspendedDMEvent; +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlRefreshAllDMEvent; import org.eclipse.cdt.dsf.debug.ui.viewmodel.SteppingController.ISteppingControlParticipant; +import org.eclipse.cdt.dsf.debug.ui.viewmodel.actions.IRefreshAllTarget; +import org.eclipse.cdt.dsf.internal.ui.DsfUIPlugin; +import org.eclipse.cdt.dsf.service.DsfServiceEventHandler; import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.dsf.ui.viewmodel.IVMAdapter; import org.eclipse.cdt.dsf.ui.viewmodel.IVMProvider; import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.AbstractDMVMAdapter; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; +import org.eclipse.jface.viewers.StructuredSelection; /** * Base class for VM adapters used for implementing a debugger integration. @@ -78,4 +88,34 @@ public class AbstractDebugVMAdapter extends AbstractDMVMAdapter implements IStep } // Do nothing if session is shut down. super.dispose(); } + + @DsfServiceEventHandler + public void eventDispatched(ICommandControlRefreshAllDMEvent event) { + if (isDisposed()) { + return; + } + + IRefreshAllTarget refreshTarget = (IRefreshAllTarget) getSession().getModelAdapter(IRefreshAllTarget.class); + if (refreshTarget == null) { + return; + } + StructuredSelection debugContext = new StructuredSelection(new IAdaptable() { + @Override + public T getAdapter(Class adapter) { + if (IVMAdapter.class.equals(adapter)) { + return adapter.cast(AbstractDebugVMAdapter.this); + } + return null; + } + }); + try { + refreshTarget.refresh(debugContext); + } catch (CoreException e) { + // This is probably unreachable as the DefaultRefreshAllTarget.refresh does not + // throw CoreException in this case. + DsfUIPlugin.log(new Status(IStatus.ERROR, DsfUIPlugin.PLUGIN_ID, + "Failed to refresh following receipt of a Refresh All Event.", e)); //$NON-NLS-1$ + + } + } } diff --git a/dsf/org.eclipse.cdt.dsf/.settings/.api_filters b/dsf/org.eclipse.cdt.dsf/.settings/.api_filters index 6e6cecefbf7..a77dda86506 100644 --- a/dsf/org.eclipse.cdt.dsf/.settings/.api_filters +++ b/dsf/org.eclipse.cdt.dsf/.settings/.api_filters @@ -22,4 +22,12 @@ + + + + + + + + diff --git a/dsf/org.eclipse.cdt.dsf/META-INF/MANIFEST.MF b/dsf/org.eclipse.cdt.dsf/META-INF/MANIFEST.MF index 72f23185492..5ca664bed4b 100644 --- a/dsf/org.eclipse.cdt.dsf/META-INF/MANIFEST.MF +++ b/dsf/org.eclipse.cdt.dsf/META-INF/MANIFEST.MF @@ -3,7 +3,7 @@ Bundle-ManifestVersion: 2 Bundle-Name: %pluginName Bundle-Vendor: %providerName Bundle-SymbolicName: org.eclipse.cdt.dsf;singleton:=true -Bundle-Version: 2.9.100.qualifier +Bundle-Version: 2.10.0.qualifier Bundle-Activator: org.eclipse.cdt.dsf.internal.DsfPlugin Bundle-Localization: plugin Require-Bundle: org.eclipse.core.runtime, diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/command/ICommandControlService.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/command/ICommandControlService.java index fe4b7aa298b..2cad8387ae4 100644 --- a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/command/ICommandControlService.java +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/command/ICommandControlService.java @@ -13,6 +13,7 @@ *******************************************************************************/ package org.eclipse.cdt.dsf.debug.service.command; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; import org.eclipse.cdt.dsf.datamodel.IDMContext; import org.eclipse.cdt.dsf.datamodel.IDMEvent; import org.eclipse.cdt.dsf.service.IDsfService; @@ -51,6 +52,13 @@ public interface ICommandControlService extends ICommandControl, IDsfService { public interface ICommandControlShutdownDMEvent extends IDMEvent { } + /** + * Event indicating that the back end has had some change that means everything should be invalidated. + * @since 2.10 + */ + public interface ICommandControlRefreshAllDMEvent extends IDMEvent { + } + /** * Returns the identifier of this command control service. It can be used * to distinguish between multiple instances of command control services. @@ -69,4 +77,14 @@ public interface ICommandControlService extends ICommandControl, IDsfService { * @return */ public boolean isActive(); + + /** + * This method should be called when a service knows that something has changed in the + * backend, but cannot update the state in an effective way, so the decision instead + * is to flush all caches and refresh by issuing an {@link ICommandControlRefreshAllDMEvent}. + * @since 2.10 + */ + default public void flushAllCachesAndRefresh(RequestMonitor rm) { + } + } diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/service/AbstractDsfService.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/service/AbstractDsfService.java index 78deefd57d1..0cc92788d57 100644 --- a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/service/AbstractDsfService.java +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/service/AbstractDsfService.java @@ -23,6 +23,7 @@ import java.util.Set; import org.eclipse.cdt.dsf.concurrent.DsfExecutor; import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants; import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.debug.service.ICachingService; import org.osgi.framework.BundleContext; import org.osgi.framework.Constants; import org.osgi.framework.ServiceRegistration; @@ -192,6 +193,13 @@ abstract public class AbstractDsfService implements IDsfService, IDsfStatusConst classSet.add(IDsfService.class.getName()); classSet.add(getClass().getName()); + /* + * Ensure that the list of classes contains the ICachingService if implemented + */ + if (this instanceof ICachingService) { + classSet.add(ICachingService.class.getName()); + } + /* * Make sure that the session ID is set in the service properties. * The session ID distinguishes this service instance from instances diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/service/DsfServicesTracker.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/service/DsfServicesTracker.java index 76971be8888..3e0822d5bc2 100644 --- a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/service/DsfServicesTracker.java +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/service/DsfServicesTracker.java @@ -15,10 +15,12 @@ package org.eclipse.cdt.dsf.service; import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.concurrent.RejectedExecutionException; +import java.util.stream.Collectors; import org.eclipse.cdt.dsf.concurrent.ConfinedToDsfExecutor; import org.eclipse.cdt.dsf.concurrent.DsfRunnable; @@ -207,7 +209,39 @@ public class DsfServicesTracker { } /** - * Convenience class to retrieve a service based on class name only. + * Retrieves all service references for given optional filter. + * Filter should be used if there are multiple instances of the desired service + * running within the same session. + * @param custom filter to use when searching for the service, this filter will + * be used instead of the standard filter so it should also specify the desired + * session-ID + * @return List of OSGI service references to the desired service + * @since 2.10 + */ + public Collection> getServiceReferences(Class serviceClass, String filter) { + if (fDisposed) { + return Collections.emptyList(); + } + + // If the session is not active, all of its services are gone. + DsfSession session = DsfSession.getSession(fSessionId); + if (session == null) { + return Collections.emptyList(); + } + assert session.getExecutor().isInExecutorThread(); + + try { + return fBundleContext.getServiceReferences(serviceClass, filter != null ? filter : fServiceFilter); + } catch (InvalidSyntaxException e) { + assert false : "Invalid session ID syntax"; //$NON-NLS-1$ + } catch (IllegalStateException e) { + // Can occur when plugin is shutting down. + } + return Collections.emptyList(); + } + + /** + * Convenience method to retrieve a service based on class name only. * @param serviceClass class of the desired service * @return instance of the desired service, null if not found */ @@ -215,6 +249,16 @@ public class DsfServicesTracker { return getService(serviceClass, null); } + /** + * Convenience method to retrieve all services based on class name only. + * @param serviceClass class of the desired service + * @return List of instances of the desired service + * @since 2.10 + */ + public Collection getServices(Class serviceClass) { + return getServices(serviceClass, null); + } + /** * Retrieves the service given service class and optional filter. * Filter should be used if there are multiple instances of the desired service @@ -231,6 +275,26 @@ public class DsfServicesTracker { return null; } + V service = getServiceHelper(serviceRef); + return service; + } + + /** + * Retrieves all services for given optional filter. + * Filter should be used if there are multiple instances of the desired service + * running within the same session. + * @param custom filter to use when searching for the service, this filter will + * be used instead of the standard filter so it should also specify the desired + * session-ID + * @return List of instances of the desired services + * @since 2.10 + */ + public Collection getServices(Class serviceClass, String filter) { + return getServiceReferences(serviceClass, filter).stream().map(this::getServiceHelper) + .collect(Collectors.toList()); + } + + private V getServiceHelper(ServiceReference serviceRef) { @SuppressWarnings("unchecked") V service = (V) fServices.get(serviceRef); if (service == null) { diff --git a/jtag/org.eclipse.cdt.debug.gdbjtag.core/src/org/eclipse/cdt/debug/gdbjtag/core/GDBJtagDSFFinalLaunchSequence.java b/jtag/org.eclipse.cdt.debug.gdbjtag.core/src/org/eclipse/cdt/debug/gdbjtag/core/GDBJtagDSFFinalLaunchSequence.java index 767fb2242dc..a9e8f9bd257 100644 --- a/jtag/org.eclipse.cdt.debug.gdbjtag.core/src/org/eclipse/cdt/debug/gdbjtag/core/GDBJtagDSFFinalLaunchSequence.java +++ b/jtag/org.eclipse.cdt.debug.gdbjtag.core/src/org/eclipse/cdt/debug/gdbjtag/core/GDBJtagDSFFinalLaunchSequence.java @@ -715,7 +715,8 @@ public class GDBJtagDSFFinalLaunchSequence extends FinalLaunchSequence { fGdbJtagDevice.doContinue(commands); queueCommands(commands, rm); } else { - rm.done(); + // Force UI to refresh collected data from target as it might have changed with complex GDB commands like 'load'. + fCommandControl.flushAllCachesAndRefresh(rm); } }