/******************************************************************************* * Copyright (c) 2012, 2015 Tilera Corporation 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: * William R. Swanson (Tilera Corporation) - initial API and implementation * IBM Corporation * Marc Dumais (Ericsson) - Bug 399281 * Marc Dumais (Ericsson) - Add CPU/core load information to the multicore visualizer (Bug 396268) * Marc Dumais (Ericsson) - Bug 399419 * Marc Dumais (Ericsson) - Bug 405390 * Marc Dumais (Ericsson) - Bug 409006 * Marc Dumais (Ericsson) - Bug 407321 * Marc-Andre Laperle (Ericsson) - Bug 411634 * Marc Dumais (Ericsson) - Bug 409965 * Xavier Raynaud (kalray) - Bug 431935 * Marc Dumais (Ericsson) - Bug 441713 * Marc Dumais (Ericsson) - Bug 442312 * Marc Dumais (Ericsson) - Bug 451392 * Marc Dumais (Ericsson) - Bug 453206 * Marc Dumais (Ericsson) - Bug 458076 * Alvaro Sanchez-Leon (Ericsson) - Bug 459114 - override construction of the data model * Marc Dumais (Ericsson) - Bug 460737 * Marc Dumais (Ericsson) - Bug 460837 * Marc Dumais (Ericsson) - Bug 460476 * Marc Khouzam (Ericsson) - Use DSF usual async pattern (Bug 459114) *******************************************************************************/ package org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.ui.view; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; import org.eclipse.cdt.dsf.concurrent.ConfinedToDsfExecutor; import org.eclipse.cdt.dsf.concurrent.CountingRequestMonitor; import org.eclipse.cdt.dsf.concurrent.DsfExecutor; import org.eclipse.cdt.dsf.concurrent.DsfRunnable; import org.eclipse.cdt.dsf.concurrent.ImmediateCountingRequestMonitor; import org.eclipse.cdt.dsf.concurrent.ImmediateDataRequestMonitor; import org.eclipse.cdt.dsf.concurrent.ImmediateRequestMonitor; import org.eclipse.cdt.dsf.concurrent.RequestMonitor; import org.eclipse.cdt.dsf.datamodel.DMContexts; import org.eclipse.cdt.dsf.datamodel.IDMContext; import org.eclipse.cdt.dsf.debug.service.IProcesses.IThreadDMData; import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMData; import org.eclipse.cdt.dsf.gdb.launching.GDBProcess; import org.eclipse.cdt.dsf.gdb.launching.GdbLaunch; import org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.ui.MulticoreVisualizerUIPlugin; import org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.ui.actions.EnableLoadMetersAction; import org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.ui.actions.FilterCanvasAction; import org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.ui.actions.PinToDebugSessionAction; import org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.ui.actions.RefreshAction; import org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.ui.actions.SelectAllAction; import org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.ui.actions.SetLoadMeterPeriodAction; import org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.ui.actions.ShowDebugToolbarAction; import org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.ui.model.VisualizerCPU; import org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.ui.model.VisualizerCore; import org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.ui.model.VisualizerExecutionState; import org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.ui.model.VisualizerLoadInfo; import org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.ui.model.VisualizerModel; import org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.ui.model.VisualizerThread; import org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.utils.DSFDebugModel; import org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.utils.DSFSessionState; import org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.utils.DebugViewUtils; import org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.utils.IDSFTargetDataProxy; import org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.utils.PersistentSettingsManager; import org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.utils.PersistentSettingsManager.PersistentParameter; import org.eclipse.cdt.dsf.gdb.service.IGDBHardwareAndOS.ICPUDMContext; import org.eclipse.cdt.dsf.gdb.service.IGDBHardwareAndOS.ICoreDMContext; import org.eclipse.cdt.dsf.gdb.service.IGDBHardwareAndOS2.ILoadInfo; import org.eclipse.cdt.dsf.mi.service.IMIExecutionDMContext; import org.eclipse.cdt.dsf.mi.service.IMIProcessDMContext; import org.eclipse.cdt.dsf.service.DsfSession; import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.IDMVMContext; import org.eclipse.cdt.visualizer.ui.canvas.GraphicCanvas; import org.eclipse.cdt.visualizer.ui.canvas.GraphicCanvasVisualizer; import org.eclipse.cdt.visualizer.ui.plugin.CDTVisualizerUIPlugin; import org.eclipse.cdt.visualizer.ui.util.Colors; import org.eclipse.cdt.visualizer.ui.util.GUIUtils; import org.eclipse.cdt.visualizer.ui.util.SelectionUtils; import org.eclipse.cdt.visualizer.ui.util.Timer; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.debug.core.ILaunch; import org.eclipse.debug.internal.ui.commands.actions.DropToFrameCommandAction; import org.eclipse.debug.internal.ui.commands.actions.ResumeCommandAction; import org.eclipse.debug.internal.ui.commands.actions.StepIntoCommandAction; import org.eclipse.debug.internal.ui.commands.actions.StepOverCommandAction; import org.eclipse.debug.internal.ui.commands.actions.StepReturnCommandAction; import org.eclipse.debug.internal.ui.commands.actions.SuspendCommandAction; import org.eclipse.debug.internal.ui.commands.actions.TerminateCommandAction; import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelChangedListener; import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta; import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelProxy; import org.eclipse.debug.internal.ui.viewers.model.provisional.TreeModelViewer; import org.eclipse.debug.internal.ui.views.launch.LaunchView; import org.eclipse.debug.ui.DebugUITools; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.action.IToolBarManager; import org.eclipse.jface.action.MenuManager; import org.eclipse.jface.action.Separator; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.widgets.Composite; /** * The Multicore Visualizer is a generic visualizer that displays * CPUs, cores, threads graphically. * * This visualizer uses the CDT Visualizer framework. */ @SuppressWarnings("restriction") public class MulticoreVisualizer extends GraphicCanvasVisualizer implements IPinnable { // --- constants --- private static final String THE_THREAD_ID_DOES_NOT_CONVERT_TO_AN_INTEGER = "The thread id does not convert to an integer: "; //$NON-NLS-1$ /** Eclipse ID for this view */ public static final String ECLIPSE_ID = "org.eclipse.cdt.dsf.gdb.multicorevisualizer.visualizer"; //$NON-NLS-1$ // --- members --- /** * The data model drawn by this visualizer. */ protected VisualizerModel fDataModel; /** * Proxy to the target data needed to build the model */ protected IDSFTargetDataProxy fTargetData; /** Downcast reference to canvas. */ protected MulticoreVisualizerCanvas m_canvas; /** DSF debug context session object. */ protected DSFSessionState m_sessionState; /** Event listener class for DSF events */ protected MulticoreVisualizerEventListener fEventListener; /** Cached reference to Debug View viewer. */ protected TreeModelViewer m_debugViewer = null; /** Model changed listener, attached to Debug View. */ protected IModelChangedListener m_modelChangedListener = null; /** Debug view selection changed listener, attached to Debug View. */ protected ISelectionChangedListener m_debugViewSelectionChangedListener = null; /** Unique id that differentiates the possible multiple instances of the MV. * It's derived from the secondary view Part id of the view associated to the * current instance of the MV. */ protected String m_visualizerInstanceId = null; // This is used to cache the CPU and core // contexts, each time the model is recreated. This way // we can avoid asking the backend for the CPU/core // geometry each time we want to update the load information. protected List<IDMContext> m_cpuCoreContextsCache = null; /** Main switch that determines if we should display the load meters */ private PersistentParameter<Boolean> m_loadMetersEnabled; /** Timer used to trigger the update of the CPU/core load meters */ protected Timer m_updateLoadMeterTimer = null; /** update period for the load meters */ private PersistentParameter<Integer> m_loadMeterTimerPeriod; // Load meters refresh periods, in ms /** constant for the very short load meters update period */ private static final int LOAD_METER_TIMER_MIN = 100; /** constant for the short load meters update period */ private static final int LOAD_METER_TIMER_FAST = 500; /** constant for the medium load meters update period */ private static final int LOAD_METER_TIMER_MEDIUM = 1000; /** constant for the long load meters update period */ private static final int LOAD_METER_TIMER_SLOW = 5000; /** Whether to show debug actions in toolbar, by default */ private static final boolean SHOW_DEBUG_ACTIONS_IN_MV_TOOLBAR_DEFAULT = true; /** Currently pinned session id, if any */ private String m_currentPinedSessionId = null; // --- UI members --- /** Whether actions have been initialized. */ protected boolean m_actionsInitialized = false; /** Toolbar / menu action */ protected Separator m_separatorAction = null; /** Toolbar / menu action */ protected ResumeCommandAction m_resumeAction = null; /** Toolbar / menu action */ protected SuspendCommandAction m_suspendAction = null; /** Toolbar / menu action */ protected TerminateCommandAction m_terminateAction = null; /** Toolbar / menu action */ protected StepReturnCommandAction m_stepReturnAction = null; /** Toolbar / menu action */ protected StepOverCommandAction m_stepOverAction = null; /** Toolbar / menu action */ protected StepIntoCommandAction m_stepIntoAction = null; /** Toolbar / menu action */ protected DropToFrameCommandAction m_dropToFrameAction = null; /** Toolbar / menu action */ protected SelectAllAction m_selectAllAction = null; /** Toolbar / menu action */ protected RefreshAction m_refreshAction = null; /** Sub-menu */ protected IMenuManager m_loadMetersSubMenu = null; /** Sub-sub menu */ protected IMenuManager m_loadMetersRefreshSubSubmenu = null; /** Menu action */ protected EnableLoadMetersAction m_enableLoadMetersAction = null; /** Menu action */ protected List<SetLoadMeterPeriodAction> m_setLoadMeterPeriodActions = null; /** Menu action */ protected FilterCanvasAction m_setFilterAction = null; /** Menu action */ protected FilterCanvasAction m_clearFilterAction = null; /** Menu action */ protected PinToDebugSessionAction m_pinToDbgSessionAction = null; /** Menu action */ protected ShowDebugToolbarAction m_showDebugToolbarAction = null; /** persistent settings manager */ protected PersistentSettingsManager m_persistentSettingsManager = null; // --- constructors/destructors --- /** Constructor. */ public MulticoreVisualizer() { fTargetData = new DSFDebugModel(); } /** Dispose method. */ @Override public void dispose() { super.dispose(); removeDebugViewerListener(); disposeActions(); disposeLoadMeterTimer(); removeEventListener(); // dispose CPU/core contexts cache if (m_cpuCoreContextsCache != null) { m_cpuCoreContextsCache.clear(); m_cpuCoreContextsCache = null; } } // --- init methods --- /** Invoked when visualizer is created, to permit any initialization. */ @Override public void initializeVisualizer() { fEventListener = new MulticoreVisualizerEventListener(this); m_cpuCoreContextsCache = new ArrayList<IDMContext>(); m_visualizerInstanceId = getViewer().getView().getViewSite().getSecondaryId(); // The first visualizer view will have a null secondary id - override that if (m_visualizerInstanceId == null) { m_visualizerInstanceId = "0"; //$NON-NLS-1$ } initializePersistentParameters(m_visualizerInstanceId); } /** * Initialize the persistent parameters */ protected void initializePersistentParameters(String visualizerInstanceId) { // setting managers m_persistentSettingsManager = new PersistentSettingsManager("MulticoreVisualizer", visualizerInstanceId); //$NON-NLS-1$ // define persistent parameters: m_loadMetersEnabled = m_persistentSettingsManager.getNewParameter(Boolean.class, "enableLoadMeters", true, false); //$NON-NLS-1$ m_loadMeterTimerPeriod = m_persistentSettingsManager.getNewParameter(Integer.class, "loadMeterTimerPeriod", true, LOAD_METER_TIMER_MEDIUM); //$NON-NLS-1$ } /** * Sets-up the timer associated to load meters refresh */ protected void initializeLoadMeterTimer() { if (!getLoadMetersEnabled()) return; m_updateLoadMeterTimer = getLoadTimer(m_sessionState, getLoadMeterTimerPeriod()); // one-shot timer (re-scheduled upon successful triggering) m_updateLoadMeterTimer.setRepeating(false); } /** * disposes of the load meter timer */ protected void disposeLoadMeterTimer() { if(m_updateLoadMeterTimer != null) { m_updateLoadMeterTimer.dispose(); m_updateLoadMeterTimer = null; } } /** Invoked when visualizer is disposed, to permit any cleanup. */ @Override public void disposeVisualizer() { // handle any other cleanup dispose(); } // --- accessors --- /** Returns non-localized unique name for this visualizer. */ @Override public String getName() { return "multicore"; //$NON-NLS-1$ } /** Returns localized name to display for this visualizer. */ @Override public String getDisplayName() { return Messages.MulticoreVisualizer_name; } /** Returns localized tooltip text to display for this visualizer. */ @Override public String getDescription() { return Messages.MulticoreVisualizer_tooltip; } /** * takes care of the details of changing the load meter timer period */ public void setLoadMeterTimerPeriod(int p) { assert (p > LOAD_METER_TIMER_MIN); if (getLoadMeterTimerPeriod() == p) return; m_loadMeterTimerPeriod.set(p > LOAD_METER_TIMER_MIN ? p : LOAD_METER_TIMER_MIN); disposeLoadMeterTimer(); initializeLoadMeterTimer(); } /** Gets the load meter period */ public int getLoadMeterTimerPeriod() { return m_loadMeterTimerPeriod != null ? m_loadMeterTimerPeriod.value() : 0; } /** * enables or disables the load meters */ public void enableLoadMeters(boolean enabled) { if (getLoadMetersEnabled() == enabled) return; setLoadMetersEnabled(enabled); // save load meter enablement in model fDataModel.setLoadMetersEnabled(getLoadMetersEnabled()); disposeLoadMeterTimer(); initializeLoadMeterTimer(); } /** Returns whether the load meters are enabled */ public boolean getLoadMetersEnabled() { return m_loadMetersEnabled != null? m_loadMetersEnabled.value() : false; } public void setLoadMetersEnabled(boolean enabled) { m_loadMetersEnabled.set(enabled); } // --- canvas management --- /** Creates and returns visualizer canvas control. */ @Override public GraphicCanvas createCanvas(Composite parent) { m_canvas = new MulticoreVisualizerCanvas(parent); m_canvas.addSelectionChangedListener(this); return m_canvas; } /** Invoked when canvas control should be disposed. */ @Override public void disposeCanvas() { if (m_canvas != null) { m_canvas.removeSelectionChangedListener(this); m_canvas.dispose(); m_canvas = null; } disposeLoadMeterTimer(); } /** Invoked after visualizer control creation, */ @Override protected void initializeCanvas(GraphicCanvas canvas) { // Any workbench views left open at application shutdown may be instanced // before our plugins are fully loaded, so make sure resource manager is initialized. // Note: this also associates the resource manager with the Colors class; // until this is done, the Colors constants are null. CDTVisualizerUIPlugin.getResources(); m_canvas.setBackground(Colors.BLACK); m_canvas.setForeground(Colors.GREEN); } /** Returns downcast reference to grid view canvas. */ public MulticoreVisualizerCanvas getMulticoreVisualizerCanvas() { return (MulticoreVisualizerCanvas) getCanvas(); } /** Sets-up a canvas filter */ public void applyCanvasFilter() { m_canvas.applyFilter(); refresh(); } /** Removes current canvas filter */ public void clearCanvasFilter() { m_canvas.clearFilter(); refresh(); } /** Tells if a canvas filter is in effect */ public boolean isCanvasFilterActive() { return m_canvas.isFilterActive(); } /** Return the data model backing this multicore visualizer */ public VisualizerModel getModel() { return fDataModel; } // --- action management --- /** Creates actions for menus/toolbar. */ protected void createActions() { if (m_actionsInitialized) return; // already done LaunchView debugView = DebugViewUtils.getDebugView(); m_separatorAction = new Separator(); m_resumeAction = new ResumeCommandAction(); if (debugView != null) m_resumeAction.init(debugView); m_suspendAction = new SuspendCommandAction(); if (debugView != null) m_suspendAction.init(debugView); m_terminateAction = new TerminateCommandAction(); if (debugView != null) m_terminateAction.init(debugView); m_stepReturnAction = new StepReturnCommandAction(); if (debugView != null) m_stepReturnAction.init(debugView); m_stepOverAction = new StepOverCommandAction(); if (debugView != null) m_stepOverAction.init(debugView); m_stepIntoAction = new StepIntoCommandAction(); if (debugView != null) m_stepIntoAction.init(debugView); m_dropToFrameAction = new DropToFrameCommandAction(); if (debugView != null) m_dropToFrameAction.init(debugView); m_selectAllAction = new SelectAllAction(); m_selectAllAction.init(this); m_refreshAction = new RefreshAction(); m_refreshAction.init(this); // create load meters sub-menu and associated actions m_loadMetersSubMenu = new MenuManager(MulticoreVisualizerUIPlugin.getString( "MulticoreVisualizer.actions.LoadMeterSubmenu.text")); //$NON-NLS-1$ m_loadMetersRefreshSubSubmenu = new MenuManager(MulticoreVisualizerUIPlugin.getString( "MulticoreVisualizer.actions.LoadMetersRefreshSubSubmenu.text")); //$NON-NLS-1$ m_enableLoadMetersAction = new EnableLoadMetersAction(getLoadMetersEnabled()); m_enableLoadMetersAction.init(this); // enable the load meter sub-menu m_enableLoadMetersAction.setEnabled(true); m_setLoadMeterPeriodActions = new ArrayList<SetLoadMeterPeriodAction>(); m_setLoadMeterPeriodActions.add(new SetLoadMeterPeriodAction( MulticoreVisualizerUIPlugin.getString("MulticoreVisualizer.actions.SetLoadMeterPeriod.fast.text"), //$NON-NLS-1$ LOAD_METER_TIMER_FAST)); // TODO: the default load meter refresh speed is set here but we could instead rely on the value saved in the data store SetLoadMeterPeriodAction defaultAction = new SetLoadMeterPeriodAction( MulticoreVisualizerUIPlugin.getString("MulticoreVisualizer.actions.SetLoadMeterPeriod.medium.text"), //$NON-NLS-1$ LOAD_METER_TIMER_MEDIUM); m_setLoadMeterPeriodActions.add(defaultAction); m_setLoadMeterPeriodActions.add(new SetLoadMeterPeriodAction( MulticoreVisualizerUIPlugin.getString("MulticoreVisualizer.actions.SetLoadMeterPeriod.slow.text"), //$NON-NLS-1$ LOAD_METER_TIMER_SLOW)); for (SetLoadMeterPeriodAction act : m_setLoadMeterPeriodActions) { act.init(this); act.setEnabled(true); } defaultAction.setChecked(true); defaultAction.run(); // canvas filter actions - they will be dynamically enabled/disabled // according to canvas selection m_setFilterAction = new FilterCanvasAction(true); m_setFilterAction.init(this); m_setFilterAction.setEnabled(false); m_clearFilterAction = new FilterCanvasAction(false); m_clearFilterAction.init(this); m_clearFilterAction.setEnabled(false); m_pinToDbgSessionAction = new PinToDebugSessionAction(); m_pinToDbgSessionAction.init(this); m_pinToDbgSessionAction.setEnabled(false); // default: do not show debug actions m_showDebugToolbarAction = new ShowDebugToolbarAction(SHOW_DEBUG_ACTIONS_IN_MV_TOOLBAR_DEFAULT, m_visualizerInstanceId); m_showDebugToolbarAction.init(this); m_showDebugToolbarAction.setEnabled(true); // Note: debug view may not be initialized at startup, // so we'll pretend the actions are not yet updated, // and reinitialize them later. m_actionsInitialized = (debugView != null); } /** Updates actions displayed on menu/toolbars. */ protected void updateActions() { if (! m_actionsInitialized) return; boolean enabled = hasSelection(); m_selectAllAction.setEnabled(enabled); m_refreshAction.setEnabled(enabled); // enable "filter-to selection" menu item if there is a // canvas selection m_setFilterAction.setEnabled(m_canvas.hasSelection()); // enable "Clear filter" menu item if filter is active m_clearFilterAction.setEnabled(isCanvasFilterActive()); // show the load meter refresh speed sub-menu only // if the load meters are enabled m_loadMetersRefreshSubSubmenu.setVisible(getLoadMetersEnabled()); // Enable pinning menu item when there is a current debug session m_pinToDbgSessionAction.setEnabled(m_sessionState != null); // We should not change the enablement of the debug view // actions, as they are automatically enabled/disabled // by the platform. } /** Updates actions specific to context menu. */ protected void updateContextMenuActions(Point location) { } /** Cleans up actions. */ protected void disposeActions() { if (!m_actionsInitialized) { return; } if (m_resumeAction != null) { m_resumeAction.dispose(); m_resumeAction = null; } if (m_suspendAction != null) { m_suspendAction.dispose(); m_suspendAction = null; } if (m_terminateAction != null) { m_terminateAction.dispose(); m_terminateAction = null; } if (m_stepReturnAction != null) { m_stepReturnAction.dispose(); m_stepReturnAction = null; } if (m_stepOverAction != null) { m_stepOverAction.dispose(); m_stepOverAction = null; } if (m_stepIntoAction != null) { m_stepIntoAction.dispose(); m_stepIntoAction = null; } if (m_dropToFrameAction != null) { m_dropToFrameAction.dispose(); m_dropToFrameAction = null; } if (m_selectAllAction != null) { m_selectAllAction.dispose(); m_selectAllAction = null; } if (m_refreshAction != null) { m_refreshAction.dispose(); m_refreshAction = null; } if (m_loadMetersSubMenu != null) { m_loadMetersSubMenu.dispose(); m_loadMetersSubMenu = null; } if (m_loadMetersRefreshSubSubmenu != null) { m_loadMetersRefreshSubSubmenu.dispose(); m_loadMetersRefreshSubSubmenu = null; } if (m_enableLoadMetersAction != null ) { m_enableLoadMetersAction.dispose(); m_enableLoadMetersAction = null; } if (m_setLoadMeterPeriodActions != null) { for (SetLoadMeterPeriodAction act : m_setLoadMeterPeriodActions) { act.dispose(); } m_setLoadMeterPeriodActions.clear(); m_setLoadMeterPeriodActions = null; } if (m_setFilterAction != null) { m_setFilterAction.dispose(); m_setFilterAction = null; } if (m_clearFilterAction != null) { m_clearFilterAction.dispose(); m_clearFilterAction = null; } if (m_pinToDbgSessionAction != null) { m_pinToDbgSessionAction.dispose(); m_pinToDbgSessionAction = null; } if (m_showDebugToolbarAction != null) { m_showDebugToolbarAction.dispose(); m_showDebugToolbarAction = null; } m_actionsInitialized = false; } // --- menu/toolbar management --- /** Invoked when visualizer is selected, to populate the toolbar. */ @Override public void populateToolBar(IToolBarManager toolBarManager) { // initialize menu/toolbar actions, if needed createActions(); // display debug buttons only if MV is not pinned // note: if in the future we want to display the debug buttons even // when pinned, all that needs to be done it to remove this check. if (!m_pinToDbgSessionAction.isChecked()) { // only show the debug actions in toolbar, if configured to do so if (m_showDebugToolbarAction.isChecked()) { toolBarManager.add(m_resumeAction); toolBarManager.add(m_suspendAction); toolBarManager.add(m_terminateAction); toolBarManager.add(m_separatorAction); toolBarManager.add(m_stepReturnAction); toolBarManager.add(m_stepOverAction); toolBarManager.add(m_stepIntoAction); toolBarManager.add(m_dropToFrameAction); } } toolBarManager.add(m_pinToDbgSessionAction); updateActions(); } /** Invoked when visualizer is selected, to populate the toolbar's menu. */ @Override public void populateMenu(IMenuManager menuManager) { // initialize menu/toolbar actions, if needed createActions(); menuManager.add(m_showDebugToolbarAction); // TODO: Anything we want to hide on the toolbar menu? updateActions(); } /** Invoked when visualizer view's context menu is invoked, to populate it. */ @Override public void populateContextMenu(IMenuManager menuManager) { // initialize menu/toolbar actions, if needed createActions(); menuManager.add(m_resumeAction); menuManager.add(m_suspendAction); menuManager.add(m_terminateAction); menuManager.add(m_separatorAction); menuManager.add(m_stepReturnAction); menuManager.add(m_stepOverAction); menuManager.add(m_stepIntoAction); menuManager.add(m_dropToFrameAction); menuManager.add(m_separatorAction); menuManager.add(m_selectAllAction); menuManager.add(m_refreshAction); menuManager.add(m_separatorAction); // add load meters sub-menus and actions m_loadMetersSubMenu.removeAll(); m_loadMetersRefreshSubSubmenu.removeAll(); menuManager.add(m_loadMetersSubMenu); m_loadMetersSubMenu.add(m_enableLoadMetersAction); m_loadMetersSubMenu.add(m_loadMetersRefreshSubSubmenu); for (SetLoadMeterPeriodAction act : m_setLoadMeterPeriodActions) { m_loadMetersRefreshSubSubmenu.add(act); } // add filtering options menuManager.add(m_separatorAction); menuManager.add(m_setFilterAction); menuManager.add(m_clearFilterAction); updateActions(); Point location = m_viewer.getContextMenuLocation(); updateContextMenuActions(location); } // --- visualizer selection management --- /** Invoked when visualizer has been selected. */ @Override public void visualizerSelected() { updateActions(); }; /** Invoked when another visualizer has been selected, hiding this one. */ @Override public void visualizerDeselected() { }; // --- workbench selection management --- /** * Tests whether if the IVisualizer can display the selection * (or something reachable from it). */ @Override public int handlesSelection(ISelection selection) { // By default, we don't support anything. int result = 0; Object sel = SelectionUtils.getSelectedObject(selection); if (sel instanceof GdbLaunch || sel instanceof GDBProcess || sel instanceof IDMVMContext) { result = 1; } else { result = 0; } // While we're here, see if we need to attach debug view listener updateDebugViewListener(); return result; } /** * Adds listener to debug view's viewer, so we can detect * Debug View updates (which it doesn't bother to properly * communicate to the rest of the world, sigh). */ protected void updateDebugViewListener() { attachDebugViewerListener(); } /** Attaches debug viewer listener. */ protected void attachDebugViewerListener() { // NOTE: debug viewer might not exist yet, so we // attach the listener at the first opportunity to do so. if (m_debugViewer == null) { m_debugViewer = DebugViewUtils.getDebugViewer(); if (m_debugViewer != null) { m_modelChangedListener = new IModelChangedListener() { @Override public void modelChanged(IModelDelta delta, IModelProxy proxy) { // Execute a refresh after any pending UI updates. GUIUtils.exec( new Runnable() { @Override public void run() { // check if we need to update the debug context updateDebugContext(); }}); } }; m_debugViewSelectionChangedListener = new ISelectionChangedListener() { @Override public void selectionChanged(SelectionChangedEvent event) { // Execute a refresh after any pending UI updates. GUIUtils.exec( new Runnable() { @Override public void run() { // Update canvas selection to match to dbg view selection updateCanvasSelectionFromDebugView(); }}); } }; m_debugViewer.addModelChangedListener(m_modelChangedListener); m_debugViewer.addSelectionChangedListener(m_debugViewSelectionChangedListener); } } } /** Removes debug viewer listener. */ protected void removeDebugViewerListener() { if (m_modelChangedListener != null && m_debugViewSelectionChangedListener != null) { if (m_debugViewer != null) { m_debugViewer.removeModelChangedListener(m_modelChangedListener); m_debugViewer.removeSelectionChangedListener(m_debugViewSelectionChangedListener); m_debugViewer = null; m_modelChangedListener = null; m_debugViewSelectionChangedListener = null; } } } private void removeEventListener() { if (m_sessionState != null) { m_sessionState.removeServiceEventListener(fEventListener); } } /** * Invoked by VisualizerViewer when workbench selection changes. */ @Override public void workbenchSelectionChanged(ISelection selection) { // See if we need to update our debug info from // the workbench selection. This will be done asynchronously. boolean changed = updateDebugContext(); if (changed) { update(); } else { // Even if debug info doesn't change, we still want to // check whether the canvas selection needs to change // to reflect the current workbench selection. updateCanvasSelection(); } // Also check whether we need to attach debug view listener. updateDebugViewListener(); } /** Refreshes visualizer content from model. */ public void refresh() { m_canvas.requestRecache(); m_canvas.requestUpdate(); } /** Updates the UI elements such as the toolbar and context menu */ public void raiseVisualizerChangedEvent() { // FIXME: replace hack below by raising a new VisualizerChanged // event, listened-to by VisualizerViewer, that causes it to raise // its own VISUALIZER_CHANGED event. See bug 442584 for details // for now do a non-change to the selection to trigger a call to // VisualizerView#updateUI() setSelection(getSelection()); } // --- ISelectionChangedListener implementation --- /** * Invoked when visualizer control's selection changes. * Sets control selection as its own selection, * and raises selection changed event for any listeners. */ @Override public void selectionChanged(SelectionChangedEvent event) { super.selectionChanged(event); // Force Debug View's selection to reflect visualizer selection, // since debug view doesn't update itself from the workbench selection. // NOTE: This can be overridden by the model selection policy, if there is one. ISelection debugViewSelection = visualizerToDebugViewSelection(getSelection()); DebugViewUtils.setDebugViewSelection(debugViewSelection); // update actions to reflect change of selection updateActions(); } // --- Selection conversion methods --- /** Gets debug view selection from visualizer selection. */ protected ISelection visualizerToDebugViewSelection(ISelection visualizerSelection) { MulticoreVisualizerSelectionFinder selectionFinder = new MulticoreVisualizerSelectionFinder(); ISelection workbenchSelection = selectionFinder.findSelection(visualizerSelection); return workbenchSelection; } /** Gets visualizer selection from debug view selection. */ protected ISelection workbenchToVisualizerSelection(ISelection workbenchSelection) { ISelection visualizerSelection = null; List<Object> items = SelectionUtils.getSelectedObjects(workbenchSelection); if (m_canvas != null) { // Use the current canvas model to match Debug View items // with corresponding threads, if any. VisualizerModel model = m_canvas.getModel(); if (model != null) { Set<Object> selected = new HashSet<Object>(); for (Object item : items) { // Currently, we ignore selections other than DSF context objects. // TODO: any other cases where we could map selections to canvas? if (item instanceof IDMVMContext) { IDMContext context = ((IDMVMContext) item).getDMContext(); IMIProcessDMContext processContext = DMContexts.getAncestorOfType(context, IMIProcessDMContext.class); int pid = Integer.parseInt(processContext.getProcId()); IMIExecutionDMContext execContext = DMContexts.getAncestorOfType(context, IMIExecutionDMContext.class); int tid = 0; if (execContext != null) { try { tid = Integer.parseInt(execContext.getThreadId()); } catch (NumberFormatException e) { // continue tid=0 assert false : THE_THREAD_ID_DOES_NOT_CONVERT_TO_AN_INTEGER + execContext.getThreadId(); } } if (tid == 0) { // process List<VisualizerThread> threads = model.getThreadsForProcess(pid); if (threads != null) { selected.addAll(threads); } } else { // thread VisualizerThread thread = model.getThread(tid); // here "tid" is the "GDB thread id", which is not // unique across sessions, so make sure the thread // belongs to the correct process, before selecting it if (thread != null && thread.getPID() == pid) { selected.add(thread); } } } } visualizerSelection = SelectionUtils.toSelection(selected); } } return visualizerSelection; } // --- IPinnable implementation --- /** * Pins the multicore visualizer to the current debug session, preventing * it from switching to a different session. */ @Override public void pin() { // No current session - do nothing if (m_sessionState == null) return; m_currentPinedSessionId = m_sessionState.getSessionID(); m_showDebugToolbarAction.setEnabled(false); } /** * Unpins the visualizer. */ @Override public void unpin() { m_currentPinedSessionId = null; // force visualizer to re-evaluate its current session and // display the correct one, if needed workbenchSelectionChanged(null); m_showDebugToolbarAction.setEnabled(true); } /** Returns whether the MV is currently pinned to a session */ @Override public boolean isPinned() { return m_currentPinedSessionId != null; } // --- DSF Context Management --- /** Updates debug context being displayed by canvas. * Returns true if canvas context actually changes, false if not. */ public boolean updateDebugContext() { // is the visualizer pinned? Then inhibit context change if (isPinned()) return false; String sessionId = null; IAdaptable debugContext = DebugUITools.getDebugContext(); if (debugContext instanceof IDMVMContext) { sessionId = ((IDMVMContext)debugContext).getDMContext().getSessionId(); } else if (debugContext instanceof GdbLaunch) { GdbLaunch gdbLaunch = (GdbLaunch)debugContext; if (gdbLaunch.isTerminated() == false) { sessionId = gdbLaunch.getSession().getId(); } } else if (debugContext instanceof GDBProcess) { ILaunch launch = ((GDBProcess)debugContext).getLaunch(); if (launch.isTerminated() == false && launch instanceof GdbLaunch) { sessionId = ((GdbLaunch)launch).getSession().getId(); } } return setDebugSession(sessionId); } /** Sets debug context being displayed by canvas. * Returns true if canvas context actually changes, false if not. */ public boolean setDebugSession(String sessionId) { boolean changed = false; if (m_sessionState != null && ! m_sessionState.getSessionID().equals(sessionId)) { // stop timer that updates the load meters disposeLoadMeterTimer(); m_sessionState.removeServiceEventListener(fEventListener); m_sessionState.dispose(); m_sessionState = null; changed = true; } if (m_sessionState == null && sessionId != null) { m_sessionState = new DSFSessionState(sessionId); m_sessionState.addServiceEventListener(fEventListener); // start timer that updates the load meters initializeLoadMeterTimer(); changed = true; } return changed; } // --- Update methods --- /** Updates visualizer canvas state. */ public void update() { // Create new VisualizerModel and hand it to canvas, // TODO: cache the VisualizerModel somehow and update it, // rather than creating it from scratch each time. if (m_sessionState == null) { // no state to display, we can immediately clear the canvas setCanvasModel(null); return; } // clear CPU/core cache m_cpuCoreContextsCache.clear(); fDataModel = new VisualizerModel(m_sessionState.getSessionID()); getVisualizerModel(fDataModel); } /** Sets canvas model. (Also updates canvas selection.) */ protected void setCanvasModel(VisualizerModel model) { final VisualizerModel model_f = model; GUIUtils.exec(new Runnable() { @Override public void run() { if(m_canvas != null) { m_canvas.setModel(model_f); // Update the canvas's selection from the current workbench selection. updateCanvasSelectionInternal(); } }}); } /** Updates canvas selection from current workbench selection. */ protected void updateCanvasSelection() { GUIUtils.exec(new Runnable() { @Override public void run() { // Update the canvas's selection from the current workbench selection. updateCanvasSelectionInternal(); }}); } /** Updates canvas selection from current workbench selection. * Note: this method assumes it is called on the UI thread. */ protected void updateCanvasSelectionInternal() { updateCanvasSelectionInternal(SelectionUtils.getWorkbenchSelection()); } /** Updates canvas selection from current debug view selection. * Note: this method assumes it is called on the UI thread. */ protected void updateCanvasSelectionFromDebugView() { updateCanvasSelectionInternal(DebugViewUtils.getDebugViewSelection()); } /** Updates canvas selection from current workbench selection. * Note: this method assumes it is called on the UI thread. */ protected void updateCanvasSelectionInternal(ISelection selection) { ISelection canvasSelection = workbenchToVisualizerSelection(selection); // canvas does not raise a selection changed event in this case // to avoid circular selection update events if (canvasSelection != null) m_canvas.setSelection(canvasSelection, false); } /** Selects all thread(s) displayed in the canvas. */ public void selectAll() { m_canvas.selectAll(); } // --- Visualizer model update methods --- /** * Starts visualizer model request. */ protected void getVisualizerModel(final VisualizerModel model) { m_sessionState.execute(new DsfRunnable() { @Override public void run() { // get model asynchronously starting at the top of the hierarchy getCPUs(model, new ImmediateRequestMonitor() { @Override protected void handleCompleted() { model.setLoadMetersEnabled(getLoadMetersEnabled()); updateLoads(model); model.sort(); setCanvasModel(model); } }); }}); } @ConfinedToDsfExecutor("getSession().getExecutor()") protected void getCPUs(final VisualizerModel model, final RequestMonitor rm) { fTargetData.getCPUs(m_sessionState, new ImmediateDataRequestMonitor<ICPUDMContext[]>() { @Override protected void handleCompleted() { ICPUDMContext[] cpuContexts = isSuccess() ? getData() : null; getCores(cpuContexts, model, rm); } }); } @ConfinedToDsfExecutor("getSession().getExecutor()") protected void getCores(ICPUDMContext[] cpuContexts, final VisualizerModel model, final RequestMonitor rm) { if (cpuContexts == null || cpuContexts.length == 0) { // Whoops, no CPU data. // We'll fake a CPU and use it to contain any cores we find. model.addCPU(new VisualizerCPU(0)); // Collect core data. fTargetData.getCores(m_sessionState, new ImmediateDataRequestMonitor<ICoreDMContext[]>() { @Override protected void handleCompleted() { // Get Cores ICoreDMContext[] coreContexts = isSuccess() ? getData() : null; ICPUDMContext cpu = null; if (coreContexts != null && coreContexts.length > 0) { // TODO: This keeps the functionality to the same level before change: 459114, // although it's noted that this does not cover the possibility to have multiple CPU's // within the list of resolved cores cpu = DMContexts.getAncestorOfType(coreContexts[0], ICPUDMContext.class); } // Continue getThreads(cpu, coreContexts, model, rm); } }); } else { // save CPU contexts m_cpuCoreContextsCache.addAll(Arrays.asList(cpuContexts)); final CountingRequestMonitor crm = new ImmediateCountingRequestMonitor(rm); crm.setDoneCount(cpuContexts.length); for (final ICPUDMContext cpuContext : cpuContexts) { int cpuID = Integer.parseInt(cpuContext.getId()); model.addCPU(new VisualizerCPU(cpuID)); // Collect core data. fTargetData.getCores(m_sessionState, cpuContext, new ImmediateDataRequestMonitor<ICoreDMContext[]>() { @Override protected void handleCompleted() { ICoreDMContext[] coreContexts = isSuccess() ? getData() : null; getThreads(cpuContext, coreContexts, model, crm); } }); } } } @ConfinedToDsfExecutor("getSession().getExecutor()") protected void getThreads(final ICPUDMContext cpuContext, ICoreDMContext[] coreContexts, final VisualizerModel model, RequestMonitor rm) { if (coreContexts == null || coreContexts.length == 0) { // no cores for this cpu context // That's fine. rm.done(); } else { // save core contexts m_cpuCoreContextsCache.addAll(Arrays.asList(coreContexts)); int cpuID = Integer.parseInt(cpuContext.getId()); VisualizerCPU cpu = model.getCPU(cpuID); final CountingRequestMonitor crm = new ImmediateCountingRequestMonitor(rm); crm.setDoneCount(coreContexts.length); for (final ICoreDMContext coreContext : coreContexts) { int coreID = Integer.parseInt(coreContext.getId()); cpu.addCore(new VisualizerCore(cpu, coreID)); // Collect thread data fTargetData.getThreads(m_sessionState, cpuContext, coreContext, new ImmediateDataRequestMonitor<IDMContext[]>() { @Override protected void handleCompleted() { IDMContext[] threadContexts = isSuccess() ? getData() : null; getThreadData(cpuContext, coreContext, threadContexts, model, crm); } }); } } } @ConfinedToDsfExecutor("getSession().getExecutor()") protected void getThreadData(final ICPUDMContext cpuContext, final ICoreDMContext coreContext, IDMContext[] threadContexts, final VisualizerModel model, RequestMonitor rm) { if (threadContexts == null || threadContexts.length == 0) { // no threads for this core // That's fine. rm.done(); } else { final CountingRequestMonitor crm = new ImmediateCountingRequestMonitor(rm); crm.setDoneCount(threadContexts.length); for (IDMContext threadContext : threadContexts) { final IMIExecutionDMContext execContext = DMContexts.getAncestorOfType(threadContext, IMIExecutionDMContext.class); // Don't add the thread to the model just yet, let's wait until we have its data and execution state. // Collect thread data fTargetData.getThreadData(m_sessionState, cpuContext, coreContext, execContext, new ImmediateDataRequestMonitor<IThreadDMData>() { @Override protected void handleCompleted() { IThreadDMData threadData = isSuccess() ? getData() : null; getThreadExecutionState(cpuContext, coreContext, execContext, threadData, model, crm); } }); } } } /** Invoked when getThreads() request completes. */ @ConfinedToDsfExecutor("getSession().getExecutor()") protected void getThreadExecutionState(final ICPUDMContext cpuContext, final ICoreDMContext coreContext, final IMIExecutionDMContext execContext, final IThreadDMData threadData, final VisualizerModel model, final RequestMonitor rm) { // Get the execution state fTargetData.getThreadExecutionState(m_sessionState, cpuContext, coreContext, execContext, threadData, new ImmediateDataRequestMonitor<VisualizerExecutionState>() { @Override protected void handleCompleted() { final VisualizerExecutionState state = isSuccess() ? getData() : null; if (state != null && !(state.equals(VisualizerExecutionState.RUNNING)) ) { // Get the frame data fTargetData.getTopFrameData(m_sessionState, execContext, new ImmediateDataRequestMonitor<IFrameDMData>() { @Override protected void handleCompleted() { IFrameDMData frameData = isSuccess() ? getData() : null; getThreadExecutionStateDone(cpuContext, coreContext, execContext, threadData, frameData, state, model, rm); } }); } else { // frame data is not valid getThreadExecutionStateDone(cpuContext, coreContext, execContext, threadData, null, state, model, rm); } } }); } /** Invoked when getThreadExecutionState() request completes. */ @ConfinedToDsfExecutor("getSession().getExecutor()") protected void getThreadExecutionStateDone(ICPUDMContext cpuContext, ICoreDMContext coreContext, IMIExecutionDMContext execContext, IThreadDMData threadData, IFrameDMData frame, VisualizerExecutionState state, VisualizerModel model, RequestMonitor rm) { int cpuID = Integer.parseInt(cpuContext.getId()); VisualizerCPU cpu = model.getCPU(cpuID); int coreID = Integer.parseInt(coreContext.getId()); VisualizerCore core = cpu.getCore(coreID); if (state == null) { // Unable to obtain execution state. Assume running state = VisualizerExecutionState.RUNNING; } IMIProcessDMContext processContext = DMContexts.getAncestorOfType(execContext, IMIProcessDMContext.class); int pid = Integer.parseInt(processContext.getProcId()); int tid; try { tid = Integer.parseInt(execContext.getThreadId()); } catch (NumberFormatException e) { rm.setStatus(new Status(IStatus.ERROR, MulticoreVisualizerUIPlugin.PLUGIN_ID, IStatus.ERROR, "Unxepected thread id format:" + execContext.getThreadId(), e)); //$NON-NLS-1$ rm.done(); assert false : THE_THREAD_ID_DOES_NOT_CONVERT_TO_AN_INTEGER + execContext.getThreadId(); return; } String osTIDValue = threadData.getId(); // If we can't get the real Linux OS tid, fallback to using the gdb thread id int osTid = (osTIDValue == null) ? tid : Integer.parseInt(osTIDValue); // add thread if not already there - there is a potential race condition where a // thread can be added twice to the model: once at model creation and once more // through the listener. Checking at both places to prevent this. VisualizerThread t = model.getThread(tid); if (t == null) { model.addThread(new VisualizerThread(core, pid, osTid, tid, state, frame)); } // if the thread is already in the model, update it's parameters. else { t.setCore(core); t.setTID(osTid); t.setState(state); t.setLocationInfo(frame); } rm.done(); } /** Updates the loads for all cpus and cores */ @ConfinedToDsfExecutor("getSession().getExecutor()") protected void updateLoads(final VisualizerModel model) { if (m_cpuCoreContextsCache.isEmpty()) { // not ready to get load info yet return; } // if meters not enabled, do not query backend if (!getLoadMetersEnabled()) { return; } final CountingRequestMonitor crm = new ImmediateCountingRequestMonitor() { @Override protected void handleSuccess() { // canvas may have been disposed since the transaction has started if (m_canvas != null) { m_canvas.refreshLoadMeters(); m_canvas.requestUpdate(); } if (m_updateLoadMeterTimer != null) { // re-start timer m_updateLoadMeterTimer.start(); } } }; crm.setDoneCount(m_cpuCoreContextsCache.size()); // ask load for each CPU and core for (final IDMContext context : m_cpuCoreContextsCache) { fTargetData.getLoad(m_sessionState, context, new ImmediateDataRequestMonitor<ILoadInfo>() { @Override protected void handleCompleted() { ILoadInfo loadInfo = isSuccess() ? getData() : null; getLoadDone(context, loadInfo, model, crm); } }); } } /** Invoked when a getLoad() request completes. */ @ConfinedToDsfExecutor("getSession().getExecutor()") protected void getLoadDone(IDMContext context, ILoadInfo load, VisualizerModel model, RequestMonitor rm) { Integer l = null; if (load != null) { l = Integer.valueOf(load.getLoad()); } // CPU context? Update the correct CPU in the model if (context instanceof ICPUDMContext) { ICPUDMContext cpuContext = (ICPUDMContext) context; VisualizerCPU cpu = model.getCPU(Integer.parseInt(cpuContext.getId())); cpu.setLoadInfo(new VisualizerLoadInfo(l)); } // Core context? Update the correct core in the model else if(context instanceof ICoreDMContext) { ICoreDMContext coreContext = (ICoreDMContext) context; VisualizerCore core = model.getCore(Integer.parseInt(coreContext.getId())); core.setLoadInfo(new VisualizerLoadInfo(l)); } rm.done(); } private Timer getLoadTimer(final DSFSessionState sessionState, final int timeout) { Timer t = new Timer(timeout) { @Override public void run() { if (sessionState != null) { DsfSession session = DsfSession.getSession(sessionState.getSessionID()); if (session != null) { DsfExecutor executor = session.getExecutor(); if (executor != null) { executor.execute(new Runnable() { @Override public void run() { updateLoads(fDataModel); } }); } } } } }; return t; } }