/******************************************************************************* * Copyright (c) 2011, 2016 Mentor Graphics 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: * Vladimir Prus (Mentor Graphics) - initial API and implementation * Teodor Madan (Freescale Semiconductor) - Bug 486521: attaching to selected process *******************************************************************************/ package org.eclipse.cdt.dsf.gdb.internal.ui.osview; import java.util.Date; import java.util.HashMap; import java.util.Map; import org.eclipse.cdt.dsf.concurrent.ConfinedToDsfExecutor; import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; import org.eclipse.cdt.dsf.concurrent.DsfExecutor; import org.eclipse.cdt.dsf.concurrent.DsfRunnable; import org.eclipse.cdt.dsf.datamodel.DataModelInitializedEvent; import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerResumedDMEvent; import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerSuspendedDMEvent; import org.eclipse.cdt.dsf.debug.service.IRunControl.IResumedDMEvent; import org.eclipse.cdt.dsf.debug.service.IRunControl.ISuspendedDMEvent; import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlDMContext; import org.eclipse.cdt.dsf.gdb.internal.ui.GdbUIPlugin; import org.eclipse.cdt.dsf.gdb.service.IGDBHardwareAndOS2; import org.eclipse.cdt.dsf.gdb.service.IGDBHardwareAndOS2.IResourceClass; import org.eclipse.cdt.dsf.gdb.service.IGDBHardwareAndOS2.IResourcesInformation; import org.eclipse.cdt.dsf.mi.service.IMIRunControl; import org.eclipse.cdt.dsf.service.DsfServiceEventHandler; import org.eclipse.cdt.dsf.service.DsfServicesTracker; import org.eclipse.cdt.dsf.service.DsfSession; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.ui.statushandlers.StatusManager; import org.osgi.framework.BundleContext; /** Responsible for fetching and storing OS awareness data for a * specific DSF session. * * @since 2.4 */ public class SessionOSData { private DsfSession fSession; private DsfServicesTracker fTracker; private IGDBHardwareAndOS2 fHardwareOs; private ICommandControlDMContext fContext; private IResourceClass[] fResourceClasses = new IResourceClass[0]; private Map<String, OSData> fExistingData = new HashMap<String, OSData>(); private Map<String, Date> fTimestamp = new HashMap<String, Date>(); private Listener fUIListener; private Control fUIControl; private boolean fWaitingForSession = true; private boolean fSupported = true; private boolean fAcceptingCommands = false; private boolean fFetchingClasses = false; private boolean fFetchingContent = false; public SessionOSData(DsfSession session, final ICommandControlDMContext executionContext) { fSession = session; BundleContext c = GdbUIPlugin.getDefault().getBundle().getBundleContext(); fTracker = new DsfServicesTracker(c, fSession.getId()); fContext = executionContext; final DsfExecutor executor = fSession.getExecutor(); executor.submit(new DsfRunnable() { @Override public void run() { IMIRunControl runControl = fTracker.getService(IMIRunControl.class); fAcceptingCommands = runControl.isTargetAcceptingCommands(); fSession.addServiceEventListener(SessionOSData.this, null); fHardwareOs = fTracker.getService(IGDBHardwareAndOS2.class); if (fHardwareOs == null) { fSupported = false; notifyUI(); return; } if (fHardwareOs.isAvailable()) { fetchClasses(); } } }); } @ConfinedToDsfExecutor("") private void fetchClasses() { fWaitingForSession = false; fFetchingClasses = true; fHardwareOs.getResourceClasses(fContext, new DataRequestMonitor<IResourceClass[]>(fSession.getExecutor(), null) { @Override @ConfinedToDsfExecutor("fExecutor") protected void handleCompleted() { if (isSuccess()) { fResourceClasses = getData(); if (fResourceClasses.length == 0) fSupported = false; } else { fSupported = false; } fFetchingClasses = false; notifyUI(); } }); } @DsfServiceEventHandler public void eventDispatched(DataModelInitializedEvent e) { // If we see this event, it necessary means that by the time we've set event listener, // isAvailable() was returning false, so we need to fetch classes now. if (fHardwareOs != null) fetchClasses(); } public boolean waitingForSessionInitialization() { return fWaitingForSession; } public boolean osResourcesSupported() { return fSupported; } public void dispose() { fSession.removeServiceEventListener(SessionOSData.this); fTracker.dispose(); } public IResourceClass[] getResourceClasses() { return fResourceClasses; } /** Returns OS awareness data for given resource class that * was previously fetched, or null if none was ever fetched. */ public OSData existingData(String resourceClass) { return fExistingData.get(resourceClass); } /** Returns the timestamp at which data for 'resourceClass' have * been obtained. * @pre existingData(resourceClass) != null */ public Date timestamp(String resourceClass) { return fTimestamp.get(resourceClass); } /** Returns true if fresh data can be fetched at this time. * Generally, it's possible if we're not fetching data already, * and if GDB is accepting commands right now. * */ public boolean canFetchData() { return fAcceptingCommands && !fFetchingContent; } public boolean fetchingClasses() { return fFetchingClasses; } /** Returns true if we're presently fetching data. This can * be used to provide some feedback to the user. */ public boolean fetchingContent() { return fFetchingContent; } /** Fetches up-to-date data for resourceClass. Listeners will be * informed when the new data is available. */ public void fetchData(final String resourceClass) { fFetchingContent = true; notifyUI(); final DsfExecutor executor = fSession.getExecutor(); executor.submit(new DsfRunnable() { @Override public void run() { fHardwareOs.getResourcesInformation(fContext, resourceClass, new DataRequestMonitor<IResourcesInformation>(executor, null) { @Override @ConfinedToDsfExecutor("fExecutor") protected void handleCompleted() { fFetchingContent = false; if (isSuccess()) { OSData data = new OSData(resourceClass, getData()); fExistingData.put(resourceClass, data); fTimestamp.put(resourceClass, new Date()); } else { StatusManager.getManager().handle(getStatus(), StatusManager.SHOW); } notifyUI(); } }); } }); } public interface Listener { void update(); } /** Setup the listener that will be notified whenever externally * visible state changes. The listener will always be invoked * in the UI thread. 'control' is the control associated with * the listener. The listener will not be called if the control * is disposed. */ public void setUIListener(Listener listener, Control control) { fUIListener = listener; fUIControl = control; } private void notifyUI() { final Control c = fUIControl; if (c != null && !c.isDisposed()) // There be dragons: if you try to use c.getDisplay() below, then this Runnable will not // run until resource view is actually visible. And it will also block other interesting // async/job runnables, like perspective switch runnable using during debug launch, // causing launch to be stuck at random point. // Display.getDefault().asyncExec(new Runnable() { @Override public void run() { if (!c.isDisposed()) fUIListener.update(); } }); } @DsfServiceEventHandler public void eventDispatched(IResumedDMEvent e) { if (e instanceof IContainerResumedDMEvent) { // This event is raised only in all-stop. It does not // seem to be possible to issue -info-os in all-stop, // regardless of whether target-async is in effect, and // according to DSF folks, all-stop+target-async will // not work anyway. So, we assume that no commands // can be issued right now. fAcceptingCommands = false; notifyUI(); } } @DsfServiceEventHandler public void eventDispatched(ISuspendedDMEvent e) { if (e instanceof IContainerSuspendedDMEvent) { fAcceptingCommands = true; notifyUI(); } } /** * @return the fContext */ public ICommandControlDMContext getContext() { return fContext; } }