/*******************************************************************************
 * 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.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 ---
	
	/** 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 = (execContext == null) ? 0 : 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 = execContext.getThreadId();
		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;
	}
}

