mirror of
https://github.com/eclipse-cdt/cdt
synced 2025-07-15 21:15:23 +02:00
Bug 396268 - [Visualizer] Add CPU/core load information to the multicore
visualizer Change-Id: I28432354b497732b4ecf7924bb7227e0b8d25508 Reviewed-on: https://git.eclipse.org/r/10077 Reviewed-by: William Swanson <traveler@tilera.com> Reviewed-by: Marc Khouzam <marc.khouzam@ericsson.com> IP-Clean: Marc Khouzam <marc.khouzam@ericsson.com> Tested-by: Marc Khouzam <marc.khouzam@ericsson.com>
This commit is contained in:
parent
52c0edd492
commit
91de353168
23 changed files with 1891 additions and 22 deletions
|
@ -7,6 +7,7 @@
|
||||||
#
|
#
|
||||||
# Contributors:
|
# Contributors:
|
||||||
# William R. Swanson (Tilera Corporation)
|
# William R. Swanson (Tilera Corporation)
|
||||||
|
# Marc Dumais (Ericsson) - Add CPU/core load information to the multicore visualizer (Bug 396268)
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
|
@ -19,3 +20,15 @@ MulticoreVisualizer.actions.SelectAll.description=Select all thread(s)
|
||||||
|
|
||||||
MulticoreVisualizer.actions.Refresh.text=Refresh@F5
|
MulticoreVisualizer.actions.Refresh.text=Refresh@F5
|
||||||
MulticoreVisualizer.actions.Refresh.description=Refresh the visualizer display
|
MulticoreVisualizer.actions.Refresh.description=Refresh the visualizer display
|
||||||
|
|
||||||
|
MulticoreVisualizer.actions.EnableLoadMeter.Enable.text=Enable Load Meters
|
||||||
|
MulticoreVisualizer.actions.EnableLoadMeter.Disable.text=Disable Load Meters
|
||||||
|
MulticoreVisualizer.actions.EnableLoadMeter.description=Enable or disable the load meters
|
||||||
|
|
||||||
|
MulticoreVisualizer.actions.SetLoadMeterPeriod.fast.text=Fast
|
||||||
|
MulticoreVisualizer.actions.SetLoadMeterPeriod.medium.text=Medium
|
||||||
|
MulticoreVisualizer.actions.SetLoadMeterPeriod.slow.text=Slow
|
||||||
|
MulticoreVisualizer.actions.SetLoadMeterPeriod.description=Choose the refresh speed for load meters
|
||||||
|
|
||||||
|
MulticoreVisualizer.actions.LoadMeterSubmenu.text=Load Meters
|
||||||
|
MulticoreVisualizer.actions.LoadMetersRefreshSubSubmenu.text=Refresh Speed
|
||||||
|
|
|
@ -0,0 +1,77 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2013 Ericsson
|
||||||
|
* 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:
|
||||||
|
* Marc Dumais (Ericsson) - Initial API and implementation (Bug 396268)
|
||||||
|
*******************************************************************************/
|
||||||
|
|
||||||
|
package org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.ui.actions;
|
||||||
|
|
||||||
|
import org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.ui.MulticoreVisualizerUIPlugin;
|
||||||
|
import org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.ui.view.MulticoreVisualizer;
|
||||||
|
import org.eclipse.cdt.visualizer.ui.VisualizerAction;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 1.1
|
||||||
|
*/
|
||||||
|
public class EnableLoadMetersAction extends VisualizerAction {
|
||||||
|
|
||||||
|
/** Visualizer instance we're associated with. */
|
||||||
|
MulticoreVisualizer m_visualizer = null;
|
||||||
|
|
||||||
|
boolean m_enabled = false;
|
||||||
|
|
||||||
|
public EnableLoadMetersAction(boolean enable) {
|
||||||
|
m_enabled = enable;
|
||||||
|
setText(getTextToDisplay());
|
||||||
|
setDescription(MulticoreVisualizerUIPlugin.getString(
|
||||||
|
"MulticoreVisualizer.actions.EnableLoadMeter.description")); //$NON-NLS-1$
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Dispose method. */
|
||||||
|
@Override
|
||||||
|
public void dispose()
|
||||||
|
{
|
||||||
|
m_visualizer = null;
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- init methods ---
|
||||||
|
|
||||||
|
/** Initializes this action for the specified view. */
|
||||||
|
public void init(MulticoreVisualizer visualizer)
|
||||||
|
{
|
||||||
|
m_visualizer = visualizer;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// --- methods ---
|
||||||
|
|
||||||
|
private String getTextToDisplay() {
|
||||||
|
if(m_enabled) {
|
||||||
|
return MulticoreVisualizerUIPlugin.getString(
|
||||||
|
"MulticoreVisualizer.actions.EnableLoadMeter.Disable.text"); //$NON-NLS-1$
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return MulticoreVisualizerUIPlugin.getString(
|
||||||
|
"MulticoreVisualizer.actions.EnableLoadMeter.Enable.text"); //$NON-NLS-1$
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Invoked when action is triggered. */
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if (m_visualizer != null) {
|
||||||
|
// toggle enabled state
|
||||||
|
m_enabled = !m_enabled;
|
||||||
|
m_visualizer.setLoadMetersEnabled(m_enabled);
|
||||||
|
m_visualizer.refresh();
|
||||||
|
|
||||||
|
setText(getTextToDisplay());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,62 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2013 Ericsson
|
||||||
|
* 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:
|
||||||
|
* Marc Dumais (Ericsson) - Initial API and implementation (Bug 396268)
|
||||||
|
*******************************************************************************/
|
||||||
|
|
||||||
|
package org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.ui.actions;
|
||||||
|
|
||||||
|
import org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.ui.MulticoreVisualizerUIPlugin;
|
||||||
|
import org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.ui.view.MulticoreVisualizer;
|
||||||
|
import org.eclipse.cdt.visualizer.ui.VisualizerAction;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 1.1
|
||||||
|
*/
|
||||||
|
public class SetLoadMeterPeriodAction extends VisualizerAction {
|
||||||
|
|
||||||
|
|
||||||
|
/** Visualizer instance we're associated with. */
|
||||||
|
MulticoreVisualizer m_visualizer = null;
|
||||||
|
|
||||||
|
final int m_period;
|
||||||
|
|
||||||
|
public SetLoadMeterPeriodAction(String label, int period) {
|
||||||
|
super(label, AS_RADIO_BUTTON);
|
||||||
|
m_period = period;
|
||||||
|
|
||||||
|
setDescription(MulticoreVisualizerUIPlugin.getString(
|
||||||
|
"MulticoreVisualizer.actions.SetLoadMeterPeriod.description")); //$NON-NLS-1$
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Dispose method. */
|
||||||
|
@Override
|
||||||
|
public void dispose()
|
||||||
|
{
|
||||||
|
m_visualizer = null;
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- init methods ---
|
||||||
|
|
||||||
|
/** Initializes this action for the specified view. */
|
||||||
|
public void init(MulticoreVisualizer visualizer)
|
||||||
|
{
|
||||||
|
m_visualizer = visualizer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Invoked when action is triggered. */
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if (!isChecked()) return;
|
||||||
|
|
||||||
|
if (m_visualizer != null) {
|
||||||
|
m_visualizer.setLoadMeterTimerPeriod(m_period);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,6 +7,7 @@
|
||||||
*
|
*
|
||||||
* Contributors:
|
* Contributors:
|
||||||
* William R. Swanson (Tilera Corporation) - initial API and implementation
|
* William R. Swanson (Tilera Corporation) - initial API and implementation
|
||||||
|
* Marc Dumais (Ericsson) - Add CPU/core load information to the multicore visualizer (Bug 396268)
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
|
||||||
package org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.ui.model;
|
package org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.ui.model;
|
||||||
|
@ -29,6 +30,11 @@ public class VisualizerCPU
|
||||||
/** ID of this core. */
|
/** ID of this core. */
|
||||||
public int m_id;
|
public int m_id;
|
||||||
|
|
||||||
|
/** Contains load information
|
||||||
|
* @since 1.1
|
||||||
|
*/
|
||||||
|
protected VisualizerLoadInfo m_loadinfo;
|
||||||
|
|
||||||
/** List of cores */
|
/** List of cores */
|
||||||
protected ArrayList<VisualizerCore> m_cores;
|
protected ArrayList<VisualizerCore> m_cores;
|
||||||
|
|
||||||
|
@ -55,6 +61,7 @@ public class VisualizerCPU
|
||||||
m_coreMap = null;
|
m_coreMap = null;
|
||||||
m_cores.clear();
|
m_cores.clear();
|
||||||
m_cores = null;
|
m_cores = null;
|
||||||
|
m_loadinfo = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,6 +82,23 @@ public class VisualizerCPU
|
||||||
return m_id;
|
return m_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** sets the load info for this CPU
|
||||||
|
* @since 1.1*/
|
||||||
|
public synchronized void setLoadInfo (VisualizerLoadInfo info) {
|
||||||
|
m_loadinfo = info;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Gets the CPU usage load of this CPU.
|
||||||
|
* @since 1.1*/
|
||||||
|
public synchronized Integer getLoad() {
|
||||||
|
return (m_loadinfo == null) ? null : m_loadinfo.getLoad();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** get the highest recorded load for this CPU
|
||||||
|
* @since 1.1*/
|
||||||
|
public synchronized Integer getHighLoadWatermark() {
|
||||||
|
return (m_loadinfo == null) ? null : m_loadinfo.getHighLoadWaterMark();
|
||||||
|
}
|
||||||
|
|
||||||
// --- methods ---
|
// --- methods ---
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
*
|
*
|
||||||
* Contributors:
|
* Contributors:
|
||||||
* William R. Swanson (Tilera Corporation) - initial API and implementation
|
* William R. Swanson (Tilera Corporation) - initial API and implementation
|
||||||
|
* Marc Dumais (Ericsson) - Add CPU/core load information to the multicore visualizer (Bug 396268)
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
|
||||||
package org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.ui.model;
|
package org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.ui.model;
|
||||||
|
@ -23,6 +24,10 @@ public class VisualizerCore
|
||||||
/** Linux CPU ID of this core. */
|
/** Linux CPU ID of this core. */
|
||||||
public int m_id = 0;
|
public int m_id = 0;
|
||||||
|
|
||||||
|
/** Contains load information
|
||||||
|
* @since 1.1
|
||||||
|
*/
|
||||||
|
protected VisualizerLoadInfo m_loadinfo;
|
||||||
|
|
||||||
// --- constructors/destructors ---
|
// --- constructors/destructors ---
|
||||||
|
|
||||||
|
@ -34,6 +39,7 @@ public class VisualizerCore
|
||||||
|
|
||||||
/** Dispose method */
|
/** Dispose method */
|
||||||
public void dispose() {
|
public void dispose() {
|
||||||
|
m_loadinfo = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -57,6 +63,24 @@ public class VisualizerCore
|
||||||
public int getID() {
|
public int getID() {
|
||||||
return m_id;
|
return m_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** sets the load info for this core
|
||||||
|
* @since 1.1*/
|
||||||
|
public synchronized void setLoadInfo (VisualizerLoadInfo info) {
|
||||||
|
m_loadinfo = info;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Gets the CPU usage load of this core.
|
||||||
|
* @since 1.1*/
|
||||||
|
public synchronized Integer getLoad() {
|
||||||
|
return (m_loadinfo == null) ? null : m_loadinfo.getLoad();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** get the highest recorded load for this core
|
||||||
|
* @since 1.1*/
|
||||||
|
public synchronized Integer getHighLoadWatermark() {
|
||||||
|
return (m_loadinfo == null) ? null : m_loadinfo.getHighLoadWaterMark();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// --- methods ---
|
// --- methods ---
|
||||||
|
|
|
@ -0,0 +1,71 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2013 Ericsson
|
||||||
|
* 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:
|
||||||
|
* Marc Dumais (Ericsson) - Initial API and implementation (Bug 396268)
|
||||||
|
*******************************************************************************/
|
||||||
|
package org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.ui.model;
|
||||||
|
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
//VisualizerLoadInfo
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 1.1
|
||||||
|
*/
|
||||||
|
public class VisualizerLoadInfo {
|
||||||
|
|
||||||
|
// --- members ---
|
||||||
|
|
||||||
|
/** load */
|
||||||
|
protected Integer m_load = null;
|
||||||
|
|
||||||
|
/** the high load water-mark */
|
||||||
|
protected Integer m_highLoadWatermark = null;
|
||||||
|
|
||||||
|
|
||||||
|
// --- constructors/destructors ---
|
||||||
|
|
||||||
|
/** constructor */
|
||||||
|
public VisualizerLoadInfo (Integer load) {
|
||||||
|
m_load = load;
|
||||||
|
}
|
||||||
|
|
||||||
|
public VisualizerLoadInfo (Integer load, Integer highLoadWatermark) {
|
||||||
|
this(load);
|
||||||
|
m_highLoadWatermark = highLoadWatermark;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// --- Object methods ---
|
||||||
|
|
||||||
|
/** Returns string representation. */
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
if(m_highLoadWatermark != null) {
|
||||||
|
return "Load:" + m_load + ", high water-mark:" + m_highLoadWatermark; //$NON-NLS-1$ //$NON-NLS-2$
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return "Load:" + m_load + ", high water-mark: not defined"; //$NON-NLS-1$ //$NON-NLS-2$
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- accessors ---
|
||||||
|
|
||||||
|
/** Gets the CPU usage load of this core. */
|
||||||
|
public Integer getLoad() {
|
||||||
|
return m_load;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** get the high load water-mark */
|
||||||
|
public Integer getHighLoadWaterMark() {
|
||||||
|
return m_highLoadWatermark;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -7,6 +7,7 @@
|
||||||
*
|
*
|
||||||
* Contributors:
|
* Contributors:
|
||||||
* William R. Swanson (Tilera Corporation) - initial API and implementation
|
* William R. Swanson (Tilera Corporation) - initial API and implementation
|
||||||
|
* Marc Dumais (Ericsson) - Initial API and implementation (Bug 396268)
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
|
||||||
package org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.ui.model;
|
package org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.ui.model;
|
||||||
|
@ -38,12 +39,17 @@ public class VisualizerModel
|
||||||
/** Completion state tracker. */
|
/** Completion state tracker. */
|
||||||
protected Todo m_todo;
|
protected Todo m_todo;
|
||||||
|
|
||||||
|
/** Completion state tracker for load meters. */
|
||||||
|
protected Todo m_loadTodo;
|
||||||
|
|
||||||
// Setting to remove exited threads, or keep them shown.
|
// Setting to remove exited threads, or keep them shown.
|
||||||
// If we are to support this, we should have a preference
|
// If we are to support this, we should have a preference
|
||||||
// and a way to for the user to clean up old threads,
|
// and a way to for the user to clean up old threads,
|
||||||
// or maybe a timeout to remove them.
|
// or maybe a timeout to remove them.
|
||||||
private boolean m_keepExitedThreads = false;
|
private boolean m_keepExitedThreads = false;
|
||||||
|
|
||||||
|
protected boolean m_loadMetersEnabled = false;
|
||||||
|
|
||||||
// --- constructors/destructors ---
|
// --- constructors/destructors ---
|
||||||
|
|
||||||
/** Constructor */
|
/** Constructor */
|
||||||
|
@ -52,6 +58,7 @@ public class VisualizerModel
|
||||||
m_cpuMap = new Hashtable<Integer, VisualizerCPU>();
|
m_cpuMap = new Hashtable<Integer, VisualizerCPU>();
|
||||||
m_threads = new ArrayList<VisualizerThread>();
|
m_threads = new ArrayList<VisualizerThread>();
|
||||||
m_todo = new Todo();
|
m_todo = new Todo();
|
||||||
|
m_loadTodo = new Todo();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Dispose method */
|
/** Dispose method */
|
||||||
|
@ -76,6 +83,10 @@ public class VisualizerModel
|
||||||
m_todo.dispose();
|
m_todo.dispose();
|
||||||
m_todo = null;
|
m_todo = null;
|
||||||
}
|
}
|
||||||
|
if (m_loadTodo != null) {
|
||||||
|
m_loadTodo.dispose();
|
||||||
|
m_loadTodo = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -86,6 +97,19 @@ public class VisualizerModel
|
||||||
return m_todo;
|
return m_todo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Gets completion state tracker. */
|
||||||
|
public Todo getLoadTodo() {
|
||||||
|
return m_loadTodo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLoadMetersEnabled (boolean enable) {
|
||||||
|
m_loadMetersEnabled = enable;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getLoadMetersEnabled () {
|
||||||
|
return m_loadMetersEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
// --- methods ---
|
// --- methods ---
|
||||||
|
|
||||||
/** Sorts cores, cpus, etc. by IDs. */
|
/** Sorts cores, cpus, etc. by IDs. */
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
*
|
*
|
||||||
* Contributors:
|
* Contributors:
|
||||||
* Marc Khouzam (Ericsson) - initial API and implementation
|
* Marc Khouzam (Ericsson) - initial API and implementation
|
||||||
|
* Marc Dumais (Ericsson) - Add CPU/core load information to the multicore visualizer (Bug 396268)
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
|
||||||
package org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.ui.view;
|
package org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.ui.view;
|
||||||
|
@ -49,6 +50,17 @@ public class IMulticoreVisualizerConstants
|
||||||
public static final Color COLOR_CRASHED_CORE_FG = Colors.RED;
|
public static final Color COLOR_CRASHED_CORE_FG = Colors.RED;
|
||||||
public static final Color COLOR_CRASHED_CORE_BG = Colors.DARK_RED;
|
public static final Color COLOR_CRASHED_CORE_BG = Colors.DARK_RED;
|
||||||
|
|
||||||
|
// Colors for drawing CPUs
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 1.1
|
||||||
|
*/
|
||||||
|
public static final Color COLOR_CPU_FG = Colors.GREEN;
|
||||||
|
/**
|
||||||
|
* @since 1.1
|
||||||
|
*/
|
||||||
|
public static final Color COLOR_CPU_BG = Colors.getColor(0,64,0);
|
||||||
|
|
||||||
// Colors for text
|
// Colors for text
|
||||||
|
|
||||||
/** Color to be used to draw a the text for a thread */
|
/** Color to be used to draw a the text for a thread */
|
||||||
|
@ -58,4 +70,28 @@ public class IMulticoreVisualizerConstants
|
||||||
/** Color to be used to draw a the text for a core */
|
/** Color to be used to draw a the text for a core */
|
||||||
public static final Color COLOR_CORE_TEXT_FG = Colors.WHITE;
|
public static final Color COLOR_CORE_TEXT_FG = Colors.WHITE;
|
||||||
public static final Color COLOR_CORE_TEXT_BG = Colors.BLACK;
|
public static final Color COLOR_CORE_TEXT_BG = Colors.BLACK;
|
||||||
|
|
||||||
|
/** Color to be used to draw the load text
|
||||||
|
* @since 1.1
|
||||||
|
*/
|
||||||
|
public static final Color COLOR_LOAD_TEXT = Colors.GREEN;
|
||||||
|
|
||||||
|
// Colors for load meters
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 1.1
|
||||||
|
*/
|
||||||
|
public static final Color COLOR_LOAD_LOADBAR_NORMAL = Colors.GREEN;
|
||||||
|
/**
|
||||||
|
* @since 1.1
|
||||||
|
*/
|
||||||
|
public static final Color COLOR_LOAD_LOADBAR_OVERLOAD = Colors.RED;
|
||||||
|
/**
|
||||||
|
* @since 1.1
|
||||||
|
*/
|
||||||
|
public static final Color COLOR_LOAD_UNDERBAR_FG = Colors.getColor(0,200,0);
|
||||||
|
/**
|
||||||
|
* @since 1.1
|
||||||
|
*/
|
||||||
|
public static final Color COLOR_LOAD_UNDERBAR_BG_DEFAULT = Colors.getColor(0,64,0);
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,10 +9,12 @@
|
||||||
* William R. Swanson (Tilera Corporation) - initial API and implementation
|
* William R. Swanson (Tilera Corporation) - initial API and implementation
|
||||||
* IBM Corporation
|
* IBM Corporation
|
||||||
* Marc Dumais (Ericsson) - Bug 399281
|
* Marc Dumais (Ericsson) - Bug 399281
|
||||||
|
* Marc Dumais (Ericsson) - Add CPU/core load information to the multicore visualizer (Bug 396268)
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
|
||||||
package org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.ui.view;
|
package org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.ui.view;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
@ -24,11 +26,15 @@ import org.eclipse.cdt.dsf.datamodel.IDMContext;
|
||||||
import org.eclipse.cdt.dsf.debug.service.IProcesses.IThreadDMData;
|
import org.eclipse.cdt.dsf.debug.service.IProcesses.IThreadDMData;
|
||||||
import org.eclipse.cdt.dsf.gdb.launching.GDBProcess;
|
import org.eclipse.cdt.dsf.gdb.launching.GDBProcess;
|
||||||
import org.eclipse.cdt.dsf.gdb.launching.GdbLaunch;
|
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.RefreshAction;
|
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.SelectAllAction;
|
||||||
|
import org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.ui.actions.SetLoadMeterPeriodAction;
|
||||||
import org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.ui.model.VisualizerCPU;
|
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.VisualizerCore;
|
||||||
import org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.ui.model.VisualizerExecutionState;
|
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.VisualizerModel;
|
||||||
import org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.ui.model.VisualizerThread;
|
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.DSFDebugModel;
|
||||||
|
@ -37,6 +43,7 @@ import org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.utils.DSFSessionStat
|
||||||
import org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.utils.DebugViewUtils;
|
import org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.utils.DebugViewUtils;
|
||||||
import org.eclipse.cdt.dsf.gdb.service.IGDBHardwareAndOS.ICPUDMContext;
|
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.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.IMIExecutionDMContext;
|
||||||
import org.eclipse.cdt.dsf.mi.service.IMIProcessDMContext;
|
import org.eclipse.cdt.dsf.mi.service.IMIProcessDMContext;
|
||||||
import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.IDMVMContext;
|
import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.IDMVMContext;
|
||||||
|
@ -46,6 +53,7 @@ import org.eclipse.cdt.visualizer.ui.plugin.CDTVisualizerUIPlugin;
|
||||||
import org.eclipse.cdt.visualizer.ui.util.Colors;
|
import org.eclipse.cdt.visualizer.ui.util.Colors;
|
||||||
import org.eclipse.cdt.visualizer.ui.util.GUIUtils;
|
import org.eclipse.cdt.visualizer.ui.util.GUIUtils;
|
||||||
import org.eclipse.cdt.visualizer.ui.util.SelectionUtils;
|
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.IAdaptable;
|
||||||
import org.eclipse.debug.core.ILaunch;
|
import org.eclipse.debug.core.ILaunch;
|
||||||
import org.eclipse.debug.internal.ui.commands.actions.DropToFrameCommandAction;
|
import org.eclipse.debug.internal.ui.commands.actions.DropToFrameCommandAction;
|
||||||
|
@ -63,6 +71,7 @@ import org.eclipse.debug.internal.ui.views.launch.LaunchView;
|
||||||
import org.eclipse.debug.ui.DebugUITools;
|
import org.eclipse.debug.ui.DebugUITools;
|
||||||
import org.eclipse.jface.action.IMenuManager;
|
import org.eclipse.jface.action.IMenuManager;
|
||||||
import org.eclipse.jface.action.IToolBarManager;
|
import org.eclipse.jface.action.IToolBarManager;
|
||||||
|
import org.eclipse.jface.action.MenuManager;
|
||||||
import org.eclipse.jface.action.Separator;
|
import org.eclipse.jface.action.Separator;
|
||||||
import org.eclipse.jface.viewers.ISelection;
|
import org.eclipse.jface.viewers.ISelection;
|
||||||
import org.eclipse.jface.viewers.SelectionChangedEvent;
|
import org.eclipse.jface.viewers.SelectionChangedEvent;
|
||||||
|
@ -107,6 +116,39 @@ public class MulticoreVisualizer extends GraphicCanvasVisualizer
|
||||||
/** Model changed listener, attached to Debug View. */
|
/** Model changed listener, attached to Debug View. */
|
||||||
protected IModelChangedListener m_modelChangedListener = null;
|
protected IModelChangedListener m_modelChangedListener = null;
|
||||||
|
|
||||||
|
// These two arrays are 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.
|
||||||
|
/**
|
||||||
|
* @since 1.1
|
||||||
|
*/
|
||||||
|
protected ICPUDMContext[] m_cpuContextsCache = null;
|
||||||
|
/**
|
||||||
|
* @since 1.1
|
||||||
|
*/
|
||||||
|
protected ICoreDMContext[] m_coreContextsCache = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main switch that determines if we should display the load meters
|
||||||
|
* @since 1.1
|
||||||
|
*/
|
||||||
|
protected boolean m_loadMetersEnabled = false;
|
||||||
|
/**
|
||||||
|
* Timer used to trigger the update of the CPU/core load meters
|
||||||
|
* @since 1.1
|
||||||
|
*/
|
||||||
|
protected Timer m_updateLoadMeterTimer = null;
|
||||||
|
/**
|
||||||
|
* @since 1.1
|
||||||
|
*/
|
||||||
|
protected int m_loadMeterTimerPeriod = LOAD_METER_TIMER_MEDIUM; // default 1000ms
|
||||||
|
// Load meters refresh periods, in ms
|
||||||
|
private static final int LOAD_METER_TIMER_MIN = 100;
|
||||||
|
private static final int LOAD_METER_TIMER_FAST = 500;
|
||||||
|
private static final int LOAD_METER_TIMER_MEDIUM = 1000;
|
||||||
|
private static final int LOAD_METER_TIMER_SLOW = 5000;
|
||||||
|
|
||||||
|
|
||||||
// --- UI members ---
|
// --- UI members ---
|
||||||
|
|
||||||
|
@ -143,6 +185,17 @@ public class MulticoreVisualizer extends GraphicCanvasVisualizer
|
||||||
/** Toolbar / menu action */
|
/** Toolbar / menu action */
|
||||||
RefreshAction m_refreshAction = null;
|
RefreshAction m_refreshAction = null;
|
||||||
|
|
||||||
|
/** Sub-menu */
|
||||||
|
IMenuManager m_loadMetersSubMenu = null;
|
||||||
|
/** Sub-sub menu */
|
||||||
|
IMenuManager m_loadMetersRefreshSubSubmenu = null;
|
||||||
|
|
||||||
|
/** Menu action */
|
||||||
|
EnableLoadMetersAction m_enableLoadMetersAction = null;
|
||||||
|
|
||||||
|
/** Menu action */
|
||||||
|
List<SetLoadMeterPeriodAction> m_setLoadMeterPeriodActions = null;
|
||||||
|
|
||||||
|
|
||||||
// --- constructors/destructors ---
|
// --- constructors/destructors ---
|
||||||
|
|
||||||
|
@ -158,6 +211,7 @@ public class MulticoreVisualizer extends GraphicCanvasVisualizer
|
||||||
super.dispose();
|
super.dispose();
|
||||||
removeDebugViewerListener();
|
removeDebugViewerListener();
|
||||||
disposeActions();
|
disposeActions();
|
||||||
|
disposeLoadMeterTimer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -169,6 +223,26 @@ public class MulticoreVisualizer extends GraphicCanvasVisualizer
|
||||||
fEventListener = new MulticoreVisualizerEventListener(this);
|
fEventListener = new MulticoreVisualizerEventListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 1.1
|
||||||
|
*/
|
||||||
|
protected void initializeLoadMeterTimer() {
|
||||||
|
if (!m_loadMetersEnabled) return;
|
||||||
|
m_updateLoadMeterTimer = DSFDebugModel.getLoadTimer(m_sessionState, m_loadMeterTimerPeriod, this);
|
||||||
|
// one-shot timer (re-scheduled upon successful triggering)
|
||||||
|
m_updateLoadMeterTimer.setRepeating(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 1.1
|
||||||
|
*/
|
||||||
|
protected void disposeLoadMeterTimer() {
|
||||||
|
if(m_updateLoadMeterTimer != null) {
|
||||||
|
m_updateLoadMeterTimer.dispose();
|
||||||
|
m_updateLoadMeterTimer = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Invoked when visualizer is disposed, to permit any cleanup. */
|
/** Invoked when visualizer is disposed, to permit any cleanup. */
|
||||||
@Override
|
@Override
|
||||||
public void disposeVisualizer()
|
public void disposeVisualizer()
|
||||||
|
@ -198,6 +272,28 @@ public class MulticoreVisualizer extends GraphicCanvasVisualizer
|
||||||
return Messages.MulticoreVisualizer_tooltip;
|
return Messages.MulticoreVisualizer_tooltip;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 1.1
|
||||||
|
*/
|
||||||
|
public void setLoadMeterTimerPeriod(int p) {
|
||||||
|
assert (p > LOAD_METER_TIMER_MIN);
|
||||||
|
if (m_loadMeterTimerPeriod == p) return;
|
||||||
|
m_loadMeterTimerPeriod = p > LOAD_METER_TIMER_MIN ? p : LOAD_METER_TIMER_MIN;
|
||||||
|
disposeLoadMeterTimer();
|
||||||
|
initializeLoadMeterTimer();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 1.1
|
||||||
|
*/
|
||||||
|
public void setLoadMetersEnabled(boolean enabled) {
|
||||||
|
if (m_loadMetersEnabled == enabled) return;
|
||||||
|
m_loadMetersEnabled = enabled;
|
||||||
|
// save load meter enablement in model
|
||||||
|
fDataModel.setLoadMetersEnabled(m_loadMetersEnabled);
|
||||||
|
disposeLoadMeterTimer();
|
||||||
|
initializeLoadMeterTimer();
|
||||||
|
}
|
||||||
|
|
||||||
// --- canvas management ---
|
// --- canvas management ---
|
||||||
|
|
||||||
|
@ -219,6 +315,7 @@ public class MulticoreVisualizer extends GraphicCanvasVisualizer
|
||||||
m_canvas.dispose();
|
m_canvas.dispose();
|
||||||
m_canvas = null;
|
m_canvas = null;
|
||||||
}
|
}
|
||||||
|
disposeLoadMeterTimer();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Invoked after visualizer control creation, */
|
/** Invoked after visualizer control creation, */
|
||||||
|
@ -285,6 +382,37 @@ public class MulticoreVisualizer extends GraphicCanvasVisualizer
|
||||||
|
|
||||||
m_refreshAction = new RefreshAction();
|
m_refreshAction = new RefreshAction();
|
||||||
m_refreshAction.init(this);
|
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(m_loadMetersEnabled);
|
||||||
|
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));
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
// Note: debug view may not be initialized at startup,
|
// Note: debug view may not be initialized at startup,
|
||||||
// so we'll pretend the actions are not yet updated,
|
// so we'll pretend the actions are not yet updated,
|
||||||
|
@ -301,6 +429,10 @@ public class MulticoreVisualizer extends GraphicCanvasVisualizer
|
||||||
m_selectAllAction.setEnabled(enabled);
|
m_selectAllAction.setEnabled(enabled);
|
||||||
m_refreshAction.setEnabled(enabled);
|
m_refreshAction.setEnabled(enabled);
|
||||||
|
|
||||||
|
// show the load meter refresh speed sub-menu only
|
||||||
|
// if the load meters are enabled
|
||||||
|
m_loadMetersRefreshSubSubmenu.setVisible(m_loadMetersEnabled);
|
||||||
|
|
||||||
// We should not change the enablement of the debug view
|
// We should not change the enablement of the debug view
|
||||||
// actions, as they are automatically enabled/disabled
|
// actions, as they are automatically enabled/disabled
|
||||||
// by the platform.
|
// by the platform.
|
||||||
|
@ -358,6 +490,30 @@ public class MulticoreVisualizer extends GraphicCanvasVisualizer
|
||||||
m_refreshAction = null;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
m_actionsInitialized = false;
|
m_actionsInitialized = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -420,6 +576,21 @@ public class MulticoreVisualizer extends GraphicCanvasVisualizer
|
||||||
menuManager.add(m_selectAllAction);
|
menuManager.add(m_selectAllAction);
|
||||||
menuManager.add(m_refreshAction);
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
updateActions();
|
updateActions();
|
||||||
Point location = m_viewer.getContextMenuLocation();
|
Point location = m_viewer.getContextMenuLocation();
|
||||||
updateContextMenuActions(location);
|
updateContextMenuActions(location);
|
||||||
|
@ -663,6 +834,11 @@ public class MulticoreVisualizer extends GraphicCanvasVisualizer
|
||||||
if (m_sessionState != null &&
|
if (m_sessionState != null &&
|
||||||
! m_sessionState.getSessionID().equals(sessionId))
|
! m_sessionState.getSessionID().equals(sessionId))
|
||||||
{
|
{
|
||||||
|
// stop timer that updates the load meters
|
||||||
|
disposeLoadMeterTimer();
|
||||||
|
m_cpuContextsCache = null;
|
||||||
|
m_coreContextsCache = null;
|
||||||
|
|
||||||
m_sessionState.removeServiceEventListener(fEventListener);
|
m_sessionState.removeServiceEventListener(fEventListener);
|
||||||
m_sessionState.dispose();
|
m_sessionState.dispose();
|
||||||
m_sessionState = null;
|
m_sessionState = null;
|
||||||
|
@ -674,6 +850,8 @@ public class MulticoreVisualizer extends GraphicCanvasVisualizer
|
||||||
{
|
{
|
||||||
m_sessionState = new DSFSessionState(sessionId);
|
m_sessionState = new DSFSessionState(sessionId);
|
||||||
m_sessionState.addServiceEventListener(fEventListener);
|
m_sessionState.addServiceEventListener(fEventListener);
|
||||||
|
// start timer that updates the load meters
|
||||||
|
initializeLoadMeterTimer();
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -762,6 +940,8 @@ public class MulticoreVisualizer extends GraphicCanvasVisualizer
|
||||||
/** Invoked when getModel() request completes. */
|
/** Invoked when getModel() request completes. */
|
||||||
@ConfinedToDsfExecutor("getSession().getExecutor()")
|
@ConfinedToDsfExecutor("getSession().getExecutor()")
|
||||||
public void getVisualizerModelDone(VisualizerModel model) {
|
public void getVisualizerModelDone(VisualizerModel model) {
|
||||||
|
fDataModel.setLoadMetersEnabled(m_loadMetersEnabled);
|
||||||
|
updateLoads();
|
||||||
model.sort();
|
model.sort();
|
||||||
setCanvasModel(model);
|
setCanvasModel(model);
|
||||||
}
|
}
|
||||||
|
@ -774,6 +954,8 @@ public class MulticoreVisualizer extends GraphicCanvasVisualizer
|
||||||
@ConfinedToDsfExecutor("getSession().getExecutor()")
|
@ConfinedToDsfExecutor("getSession().getExecutor()")
|
||||||
public void getCPUsDone(ICPUDMContext[] cpuContexts, Object arg)
|
public void getCPUsDone(ICPUDMContext[] cpuContexts, Object arg)
|
||||||
{
|
{
|
||||||
|
// save CPU contexts
|
||||||
|
m_cpuContextsCache = cpuContexts;
|
||||||
VisualizerModel model = (VisualizerModel) arg;
|
VisualizerModel model = (VisualizerModel) arg;
|
||||||
|
|
||||||
if (cpuContexts == null || cpuContexts.length == 0) {
|
if (cpuContexts == null || cpuContexts.length == 0) {
|
||||||
|
@ -811,6 +993,8 @@ public class MulticoreVisualizer extends GraphicCanvasVisualizer
|
||||||
ICoreDMContext[] coreContexts,
|
ICoreDMContext[] coreContexts,
|
||||||
Object arg)
|
Object arg)
|
||||||
{
|
{
|
||||||
|
// save core contexts
|
||||||
|
m_coreContextsCache = coreContexts;
|
||||||
VisualizerModel model = (VisualizerModel) arg;
|
VisualizerModel model = (VisualizerModel) arg;
|
||||||
|
|
||||||
if (coreContexts == null || coreContexts.length == 0) {
|
if (coreContexts == null || coreContexts.length == 0) {
|
||||||
|
@ -924,6 +1108,70 @@ public class MulticoreVisualizer extends GraphicCanvasVisualizer
|
||||||
done(1, model);
|
done(1, model);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 1.1
|
||||||
|
*/
|
||||||
|
@ConfinedToDsfExecutor("getSession().getExecutor()")
|
||||||
|
@Override
|
||||||
|
public void updateLoads() {
|
||||||
|
if (m_cpuContextsCache == null || m_coreContextsCache == null) {
|
||||||
|
// not ready to get load info yet
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// if meters not enabled, do not query backend
|
||||||
|
if (!m_loadMetersEnabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
VisualizerModel model = fDataModel;
|
||||||
|
|
||||||
|
// keep track of how many loads we expect
|
||||||
|
int count = m_cpuContextsCache.length + m_coreContextsCache.length;
|
||||||
|
model.getLoadTodo().dispose();
|
||||||
|
model.getLoadTodo().add(count);
|
||||||
|
|
||||||
|
// ask load for each CPU
|
||||||
|
for (ICPUDMContext cpuCtx : m_cpuContextsCache) {
|
||||||
|
DSFDebugModel.getLoad(m_sessionState, cpuCtx, this, model);
|
||||||
|
}
|
||||||
|
// ask load for each core
|
||||||
|
for (ICoreDMContext coreCtx : m_coreContextsCache) {
|
||||||
|
DSFDebugModel.getLoad(m_sessionState, coreCtx, this, model);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoked when a getLoad() request completes.
|
||||||
|
* @since 1.1*/
|
||||||
|
@Override
|
||||||
|
@ConfinedToDsfExecutor("getSession().getExecutor()")
|
||||||
|
public void getLoadDone(IDMContext context, ILoadInfo load, Object arg)
|
||||||
|
{
|
||||||
|
VisualizerModel model = (VisualizerModel) arg;
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
loadDone(1, model);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/** Update "done" count for current visualizer model. */
|
/** Update "done" count for current visualizer model. */
|
||||||
protected void done(int n, VisualizerModel model) {
|
protected void done(int n, VisualizerModel model) {
|
||||||
model.getTodo().done(n);
|
model.getTodo().done(n);
|
||||||
|
@ -931,5 +1179,21 @@ public class MulticoreVisualizer extends GraphicCanvasVisualizer
|
||||||
getVisualizerModelDone(model);
|
getVisualizerModelDone(model);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Update "done" count for current visualizer model. */
|
||||||
|
protected void loadDone(int n, VisualizerModel model) {
|
||||||
|
model.getLoadTodo().done(n);
|
||||||
|
if (model.getLoadTodo().isDone()) {
|
||||||
|
// 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
*
|
*
|
||||||
* Contributors:
|
* Contributors:
|
||||||
* William R. Swanson (Tilera Corporation) - initial API and implementation
|
* William R. Swanson (Tilera Corporation) - initial API and implementation
|
||||||
|
* Marc Dumais (Ericsson) - Add CPU/core load information to the multicore visualizer (Bug 396268)
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
|
||||||
package org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.ui.view;
|
package org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.ui.view;
|
||||||
|
@ -14,7 +15,6 @@ package org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.ui.view;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.eclipse.cdt.visualizer.ui.util.Colors;
|
|
||||||
import org.eclipse.cdt.visualizer.ui.util.GUIUtils;
|
import org.eclipse.cdt.visualizer.ui.util.GUIUtils;
|
||||||
import org.eclipse.swt.graphics.Color;
|
import org.eclipse.swt.graphics.Color;
|
||||||
import org.eclipse.swt.graphics.GC;
|
import org.eclipse.swt.graphics.GC;
|
||||||
|
@ -32,6 +32,22 @@ public class MulticoreVisualizerCPU extends MulticoreVisualizerGraphicObject
|
||||||
/** Child cores. */
|
/** Child cores. */
|
||||||
protected ArrayList<MulticoreVisualizerCore> m_cores;
|
protected ArrayList<MulticoreVisualizerCore> m_cores;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load meter associated to this CPU
|
||||||
|
* @since 1.1
|
||||||
|
*/
|
||||||
|
protected MulticoreVisualizerLoadMeter m_loadMeter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 1.1
|
||||||
|
*/
|
||||||
|
protected static final Color BG_COLOR = IMulticoreVisualizerConstants.COLOR_CPU_BG;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 1.1
|
||||||
|
*/
|
||||||
|
protected static final Color FG_COLOR = IMulticoreVisualizerConstants.COLOR_CPU_FG;
|
||||||
|
|
||||||
|
|
||||||
// --- constructors/destructors ---
|
// --- constructors/destructors ---
|
||||||
|
|
||||||
|
@ -40,12 +56,18 @@ public class MulticoreVisualizerCPU extends MulticoreVisualizerGraphicObject
|
||||||
{
|
{
|
||||||
m_id = id;
|
m_id = id;
|
||||||
m_cores = new ArrayList<MulticoreVisualizerCore>();
|
m_cores = new ArrayList<MulticoreVisualizerCore>();
|
||||||
|
|
||||||
|
// default load meter
|
||||||
|
m_loadMeter = new MulticoreVisualizerLoadMeter(null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Dispose method */
|
/** Dispose method */
|
||||||
@Override
|
@Override
|
||||||
public void dispose() {
|
public void dispose() {
|
||||||
super.dispose();
|
super.dispose();
|
||||||
|
if (m_loadMeter != null) {
|
||||||
|
m_loadMeter.dispose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -77,17 +99,30 @@ public class MulticoreVisualizerCPU extends MulticoreVisualizerGraphicObject
|
||||||
return m_cores;
|
return m_cores;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 1.1
|
||||||
|
*/
|
||||||
|
public void setLoadMeter (MulticoreVisualizerLoadMeter meter) {
|
||||||
|
m_loadMeter = meter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 1.1
|
||||||
|
*/
|
||||||
|
public MulticoreVisualizerLoadMeter getLoadMeter() {
|
||||||
|
return m_loadMeter;
|
||||||
|
}
|
||||||
|
|
||||||
// --- paint methods ---
|
// --- paint methods ---
|
||||||
|
|
||||||
/** Invoked to allow element to paint itself on the viewer canvas */
|
/** Invoked to allow element to paint itself on the viewer canvas */
|
||||||
@Override
|
@Override
|
||||||
public void paintContent(GC gc) {
|
public void paintContent(GC gc) {
|
||||||
Color fg, bg;
|
gc.setForeground(FG_COLOR);
|
||||||
fg = Colors.getColor(0,255,0);
|
gc.setBackground(BG_COLOR);
|
||||||
bg = Colors.getColor(0,64,0);
|
|
||||||
gc.setForeground(fg);
|
// We want the load meter to share the same BG color
|
||||||
gc.setBackground(bg);
|
m_loadMeter.setParentBgColor(BG_COLOR);
|
||||||
|
|
||||||
gc.fillRectangle(m_bounds);
|
gc.fillRectangle(m_bounds);
|
||||||
gc.drawRectangle(m_bounds);
|
gc.drawRectangle(m_bounds);
|
||||||
|
@ -103,15 +138,13 @@ public class MulticoreVisualizerCPU extends MulticoreVisualizerGraphicObject
|
||||||
@Override
|
@Override
|
||||||
public void paintDecorations(GC gc) {
|
public void paintDecorations(GC gc) {
|
||||||
if (m_bounds.height > 20) {
|
if (m_bounds.height > 20) {
|
||||||
Color fg, bg;
|
gc.setForeground(IMulticoreVisualizerConstants.COLOR_CPU_FG);
|
||||||
fg = Colors.getColor(0,255,0);
|
gc.setBackground(IMulticoreVisualizerConstants.COLOR_CPU_BG);
|
||||||
bg = Colors.getColor(0,64,0);
|
|
||||||
gc.setForeground(fg);
|
|
||||||
gc.setBackground(bg);
|
|
||||||
|
|
||||||
int text_indent = 6;
|
int text_indent_x = 6;
|
||||||
int tx = m_bounds.x + m_bounds.width - text_indent;
|
int text_indent_y = 2;
|
||||||
int ty = m_bounds.y + m_bounds.height - text_indent;
|
int tx = m_bounds.x + m_bounds.width - text_indent_x;
|
||||||
|
int ty = m_bounds.y + m_bounds.height - text_indent_y;
|
||||||
GUIUtils.drawTextAligned(gc, Integer.toString(m_id), tx, ty, false, false);
|
GUIUtils.drawTextAligned(gc, Integer.toString(m_id), tx, ty, false, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,11 +12,13 @@
|
||||||
* Marc Dumais (Ericsson) - Bug 396200
|
* Marc Dumais (Ericsson) - Bug 396200
|
||||||
* Marc Dumais (Ericsson) - Bug 396293
|
* Marc Dumais (Ericsson) - Bug 396293
|
||||||
* Marc Dumais (Ericsson) - Bug 399281
|
* Marc Dumais (Ericsson) - Bug 399281
|
||||||
|
* Marc Dumais (Ericsson) - Add CPU/core load information to the multicore visualizer (Bug 396268)
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
|
||||||
package org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.ui.view;
|
package org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.ui.view;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Enumeration;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Hashtable;
|
import java.util.Hashtable;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -76,6 +78,12 @@ public class MulticoreVisualizerCanvas extends GraphicCanvas
|
||||||
*/
|
*/
|
||||||
protected boolean m_recacheSizes = true;
|
protected boolean m_recacheSizes = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the load information has changed and we need to update the load meters
|
||||||
|
* @since 1.1
|
||||||
|
*/
|
||||||
|
protected boolean m_recacheLoadMeters = true;
|
||||||
|
|
||||||
/** Whether we need to repaint the canvas */
|
/** Whether we need to repaint the canvas */
|
||||||
protected boolean m_update = true;
|
protected boolean m_update = true;
|
||||||
|
|
||||||
|
@ -283,6 +291,13 @@ public class MulticoreVisualizerCanvas extends GraphicCanvas
|
||||||
requestUpdate();
|
requestUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* only update the load meters
|
||||||
|
* @since 1.1
|
||||||
|
*/
|
||||||
|
public void refreshLoadMeters() {
|
||||||
|
requestRecache(false, false, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// --- resize methods ---
|
// --- resize methods ---
|
||||||
|
@ -316,16 +331,26 @@ public class MulticoreVisualizerCanvas extends GraphicCanvas
|
||||||
/** Requests that next paint call should recache state and/or size information */
|
/** Requests that next paint call should recache state and/or size information */
|
||||||
// synchronized so we don't change recache flags while doing a recache
|
// synchronized so we don't change recache flags while doing a recache
|
||||||
public synchronized void requestRecache() {
|
public synchronized void requestRecache() {
|
||||||
requestRecache(true, true);
|
requestRecache(true, true, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Requests that next paint call should recache state and/or size information */
|
/** Requests that next paint call should recache state and/or size information */
|
||||||
// synchronized so we don't change recache flags while doing a recache
|
// synchronized so we don't change recache flags while doing a recache
|
||||||
public synchronized void requestRecache(boolean state, boolean sizes) {
|
public synchronized void requestRecache(boolean state, boolean sizes) {
|
||||||
|
requestRecache(state, sizes, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Requests that the next paing call should recache state and/or size and/or load information
|
||||||
|
* @since 1.1
|
||||||
|
*/
|
||||||
|
// synchronized so we don't change recache flags while doing a recache
|
||||||
|
public synchronized void requestRecache(boolean state, boolean sizes, boolean load) {
|
||||||
m_recache = true;
|
m_recache = true;
|
||||||
// NOTE: we intentionally OR these flags with any pending request(s)
|
// NOTE: we intentionally OR these flags with any pending request(s)
|
||||||
m_recacheState |= state;
|
m_recacheState |= state;
|
||||||
m_recacheSizes |= sizes;
|
m_recacheSizes |= sizes;
|
||||||
|
m_recacheLoadMeters |= load;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Fits n square items into a rectangle of the specified size.
|
/** Fits n square items into a rectangle of the specified size.
|
||||||
|
@ -372,6 +397,7 @@ public class MulticoreVisualizerCanvas extends GraphicCanvas
|
||||||
m_threads.clear();
|
m_threads.clear();
|
||||||
m_cpuMap.clear();
|
m_cpuMap.clear();
|
||||||
m_coreMap.clear();
|
m_coreMap.clear();
|
||||||
|
m_threadMap.clear();
|
||||||
|
|
||||||
// For debugging purposes only, allows us to force a CPU count.
|
// For debugging purposes only, allows us to force a CPU count.
|
||||||
//int cpu_count = 0;
|
//int cpu_count = 0;
|
||||||
|
@ -399,11 +425,40 @@ public class MulticoreVisualizerCanvas extends GraphicCanvas
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// we've recached state, which implies recacheing sizes too
|
// we've recached state, which implies recacheing sizes and load meters
|
||||||
m_recacheState = false;
|
m_recacheState = false;
|
||||||
|
m_recacheLoadMeters = true;
|
||||||
m_recacheSizes = true;
|
m_recacheSizes = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_recacheLoadMeters) {
|
||||||
|
// refresh the visualizer CPU and core load meters
|
||||||
|
if (m_model != null) {
|
||||||
|
Enumeration<VisualizerCPU> modelCpus = m_cpuMap.keys();
|
||||||
|
while (modelCpus.hasMoreElements()) {
|
||||||
|
VisualizerCPU modelCpu = modelCpus.nextElement();
|
||||||
|
MulticoreVisualizerCPU visualizerCpu = m_cpuMap.get(modelCpu);
|
||||||
|
// update CPUs load meter
|
||||||
|
MulticoreVisualizerLoadMeter meter = visualizerCpu.getLoadMeter();
|
||||||
|
meter.setEnabled(m_model.getLoadMetersEnabled());
|
||||||
|
meter.setLoad(modelCpu.getLoad());
|
||||||
|
meter.setHighLoadWatermark(modelCpu.getHighLoadWatermark());
|
||||||
|
|
||||||
|
for (VisualizerCore modelCore : modelCpu.getCores()) {
|
||||||
|
MulticoreVisualizerCore visualizerCore = m_coreMap.get(modelCore);
|
||||||
|
// update cores load meter
|
||||||
|
meter = visualizerCore.getLoadMeter();
|
||||||
|
meter.setEnabled(m_model.getLoadMetersEnabled());
|
||||||
|
meter.setLoad(modelCore.getLoad());
|
||||||
|
meter.setHighLoadWatermark(modelCore.getHighLoadWatermark());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_recacheSizes = true;
|
||||||
|
m_recacheLoadMeters = false;
|
||||||
|
}
|
||||||
|
|
||||||
if (m_recacheSizes) {
|
if (m_recacheSizes) {
|
||||||
// avoid doing resize calculations if the model is not ready
|
// avoid doing resize calculations if the model is not ready
|
||||||
if (m_model == null ) {
|
if (m_model == null ) {
|
||||||
|
@ -416,9 +471,14 @@ public class MulticoreVisualizerCanvas extends GraphicCanvas
|
||||||
int cpu_margin = 8; // margin around edges of CPU grid
|
int cpu_margin = 8; // margin around edges of CPU grid
|
||||||
int cpu_separation = 6; // spacing between CPUS
|
int cpu_separation = 6; // spacing between CPUS
|
||||||
|
|
||||||
int core_margin = 4; // margin around cores in a CPU
|
// make room when load meters are present, else use a more compact layout
|
||||||
int core_separation = 2; // spacing between cores
|
int core_margin = m_model.getLoadMetersEnabled() ? 20 : 4; // margin around cores in a CPU
|
||||||
|
int core_separation = m_model.getLoadMetersEnabled() ? 4 : 2; // spacing between cores
|
||||||
|
|
||||||
|
int loadMeterWidth = core_margin*3/5;
|
||||||
|
int loadMeterHMargin = core_margin/5;
|
||||||
|
int loadMeterHCoreMargin = loadMeterHMargin + 5;
|
||||||
|
|
||||||
// Get overall area we have for laying out content.
|
// Get overall area we have for laying out content.
|
||||||
Rectangle bounds = getClientArea();
|
Rectangle bounds = getClientArea();
|
||||||
GUIUtils.inset(bounds, cpu_margin);
|
GUIUtils.inset(bounds, cpu_margin);
|
||||||
|
@ -442,6 +502,8 @@ public class MulticoreVisualizerCanvas extends GraphicCanvas
|
||||||
int x = bounds.x, y = bounds.y;
|
int x = bounds.x, y = bounds.y;
|
||||||
for (MulticoreVisualizerCPU cpu : m_cpus) {
|
for (MulticoreVisualizerCPU cpu : m_cpus) {
|
||||||
cpu.setBounds(x, y, cpu_size-1, cpu_size-1);
|
cpu.setBounds(x, y, cpu_size-1, cpu_size-1);
|
||||||
|
// put cpu meter in the right margin of the CPU
|
||||||
|
cpu.getLoadMeter().setBounds(x + cpu_size - 2*cpu_margin, y + 2*core_margin, loadMeterWidth, cpu_size-3*core_margin);
|
||||||
|
|
||||||
int left = x + core_margin;
|
int left = x + core_margin;
|
||||||
int cx = left, cy = y + core_margin;
|
int cx = left, cy = y + core_margin;
|
||||||
|
@ -449,6 +511,13 @@ public class MulticoreVisualizerCanvas extends GraphicCanvas
|
||||||
{
|
{
|
||||||
core.setBounds(cx, cy, core_size, core_size);
|
core.setBounds(cx, cy, core_size, core_size);
|
||||||
|
|
||||||
|
core.getLoadMeter().setBounds(
|
||||||
|
cx + core_size - loadMeterHCoreMargin - loadMeterWidth,
|
||||||
|
cy + core_size * 1 / 3,
|
||||||
|
loadMeterWidth,
|
||||||
|
core_size * 2 / 3 - loadMeterHCoreMargin
|
||||||
|
);
|
||||||
|
|
||||||
cx += core_size + core_separation;
|
cx += core_size + core_separation;
|
||||||
if (cx + core_size + core_margin > x + cpu_size) {
|
if (cx + core_size + core_margin > x + cpu_size) {
|
||||||
cx = left;
|
cx = left;
|
||||||
|
@ -564,11 +633,15 @@ public class MulticoreVisualizerCanvas extends GraphicCanvas
|
||||||
// paint cpus
|
// paint cpus
|
||||||
for (MulticoreVisualizerCPU cpu : m_cpus) {
|
for (MulticoreVisualizerCPU cpu : m_cpus) {
|
||||||
cpu.paintContent(gc);
|
cpu.paintContent(gc);
|
||||||
|
cpu.getLoadMeter().paintContent(gc);
|
||||||
|
cpu.getLoadMeter().paintDecorations(gc);
|
||||||
}
|
}
|
||||||
|
|
||||||
// paint cores
|
// paint cores
|
||||||
for (MulticoreVisualizerCore core : m_cores) {
|
for (MulticoreVisualizerCore core : m_cores) {
|
||||||
core.paintContent(gc);
|
core.paintContent(gc);
|
||||||
|
core.getLoadMeter().paintContent(gc);
|
||||||
|
core.getLoadMeter().paintDecorations(gc);
|
||||||
}
|
}
|
||||||
|
|
||||||
// paint cpus IDs on top of cores
|
// paint cpus IDs on top of cores
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
*
|
*
|
||||||
* Contributors:
|
* Contributors:
|
||||||
* William R. Swanson (Tilera Corporation) - initial API and implementation
|
* William R. Swanson (Tilera Corporation) - initial API and implementation
|
||||||
|
* Marc Dumais (Ericsson) - Add CPU/core load information to the multicore visualizer (Bug 396268)
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
|
||||||
package org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.ui.view;
|
package org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.ui.view;
|
||||||
|
@ -36,6 +37,12 @@ public class MulticoreVisualizerCore extends MulticoreVisualizerGraphicObject
|
||||||
/** List of threads currently on this core. */
|
/** List of threads currently on this core. */
|
||||||
protected ArrayList<MulticoreVisualizerThread> m_threads;
|
protected ArrayList<MulticoreVisualizerThread> m_threads;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load meter associated to this core
|
||||||
|
* @since 1.1
|
||||||
|
*/
|
||||||
|
protected MulticoreVisualizerLoadMeter m_loadMeter;
|
||||||
|
|
||||||
// --- constructors/destructors ---
|
// --- constructors/destructors ---
|
||||||
|
|
||||||
/** Constructor */
|
/** Constructor */
|
||||||
|
@ -44,6 +51,9 @@ public class MulticoreVisualizerCore extends MulticoreVisualizerGraphicObject
|
||||||
if (m_cpu != null) m_cpu.addCore(this);
|
if (m_cpu != null) m_cpu.addCore(this);
|
||||||
m_id = id;
|
m_id = id;
|
||||||
m_threads = new ArrayList<MulticoreVisualizerThread>();
|
m_threads = new ArrayList<MulticoreVisualizerThread>();
|
||||||
|
|
||||||
|
// default load meter
|
||||||
|
m_loadMeter = new MulticoreVisualizerLoadMeter(null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Dispose method */
|
/** Dispose method */
|
||||||
|
@ -54,6 +64,9 @@ public class MulticoreVisualizerCore extends MulticoreVisualizerGraphicObject
|
||||||
m_threads.clear();
|
m_threads.clear();
|
||||||
m_threads = null;
|
m_threads = null;
|
||||||
}
|
}
|
||||||
|
if (m_loadMeter != null) {
|
||||||
|
m_loadMeter.dispose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -95,6 +108,20 @@ public class MulticoreVisualizerCore extends MulticoreVisualizerGraphicObject
|
||||||
{
|
{
|
||||||
return m_threads;
|
return m_threads;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 1.1
|
||||||
|
*/
|
||||||
|
public void setLoadMeter (MulticoreVisualizerLoadMeter meter) {
|
||||||
|
m_loadMeter = meter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 1.1
|
||||||
|
*/
|
||||||
|
public MulticoreVisualizerLoadMeter getLoadMeter() {
|
||||||
|
return m_loadMeter;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A core state is based on its thread states.
|
* A core state is based on its thread states.
|
||||||
|
@ -149,8 +176,12 @@ public class MulticoreVisualizerCore extends MulticoreVisualizerGraphicObject
|
||||||
/** Invoked to allow element to paint itself on the viewer canvas */
|
/** Invoked to allow element to paint itself on the viewer canvas */
|
||||||
@Override
|
@Override
|
||||||
public void paintContent(GC gc) {
|
public void paintContent(GC gc) {
|
||||||
|
Color bg = getCoreStateColor(false);
|
||||||
|
|
||||||
gc.setForeground(getCoreStateColor(true));
|
gc.setForeground(getCoreStateColor(true));
|
||||||
gc.setBackground(getCoreStateColor(false));
|
gc.setBackground(bg);
|
||||||
|
// We want the load meter to share the same BG color
|
||||||
|
m_loadMeter.setParentBgColor(bg);
|
||||||
|
|
||||||
gc.fillRectangle(m_bounds);
|
gc.fillRectangle(m_bounds);
|
||||||
gc.drawRectangle(m_bounds);
|
gc.drawRectangle(m_bounds);
|
||||||
|
|
|
@ -0,0 +1,223 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2013 Ericsson
|
||||||
|
* 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:
|
||||||
|
* Marc Dumais (Ericsson) - Initial API and implementation (Bug 396268)
|
||||||
|
*******************************************************************************/
|
||||||
|
package org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.ui.view;
|
||||||
|
|
||||||
|
import org.eclipse.cdt.visualizer.ui.util.Colors;
|
||||||
|
import org.eclipse.cdt.visualizer.ui.util.GUIUtils;
|
||||||
|
import org.eclipse.swt.graphics.Color;
|
||||||
|
import org.eclipse.swt.graphics.GC;
|
||||||
|
import org.eclipse.swt.graphics.Rectangle;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 1.1
|
||||||
|
*/
|
||||||
|
public class MulticoreVisualizerLoadMeter extends MulticoreVisualizerGraphicObject {
|
||||||
|
|
||||||
|
// --- members ---
|
||||||
|
|
||||||
|
/** Is this load meter enabled? */
|
||||||
|
protected boolean m_enabled = false;
|
||||||
|
|
||||||
|
/** The current CPU/core load */
|
||||||
|
protected Integer m_currentLoad = null;
|
||||||
|
|
||||||
|
/** the high load water-mark */
|
||||||
|
protected Integer m_highLoadWatermark = null;
|
||||||
|
|
||||||
|
/** second rectangle, that will be displayed to show the load */
|
||||||
|
protected Rectangle m_loadRect = null;
|
||||||
|
|
||||||
|
/** to display the high load water-mark */
|
||||||
|
protected Rectangle m_highWatermarkRect = null;
|
||||||
|
|
||||||
|
/** Switch that permits to hide the load meter when not in overload */
|
||||||
|
protected Boolean m_showOnlyIfOverload = false;
|
||||||
|
|
||||||
|
/** Default overload threshold */
|
||||||
|
protected int m_overloadThreshold = 75;
|
||||||
|
|
||||||
|
/** Permits to have the load meter use the same BG color as its parent */
|
||||||
|
protected Color m_parentBgColor = null;
|
||||||
|
|
||||||
|
|
||||||
|
// --- constructors/destructors ---
|
||||||
|
|
||||||
|
/** Constructor */
|
||||||
|
|
||||||
|
public MulticoreVisualizerLoadMeter(Integer load) {
|
||||||
|
m_currentLoad = load;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Constructor witch includes the high load water-mark */
|
||||||
|
public MulticoreVisualizerLoadMeter(Integer load, Integer highWatermark) {
|
||||||
|
this(load);
|
||||||
|
m_highLoadWatermark = highWatermark;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Dispose method */
|
||||||
|
@Override
|
||||||
|
public void dispose() {
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// --- accessors ---
|
||||||
|
|
||||||
|
public void setEnabled (boolean enabled) {
|
||||||
|
m_enabled = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getEnabled() {
|
||||||
|
return m_enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLoad(Integer load) {
|
||||||
|
m_currentLoad = load;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the load value. If the value is undefined (null), zero is
|
||||||
|
* returned. Method isLoadDefined() can be used to determine is the load
|
||||||
|
* value is defined, in the cases where the difference is important
|
||||||
|
* if needed.
|
||||||
|
*/
|
||||||
|
public int getLoad() {
|
||||||
|
return (m_currentLoad != null) ? m_currentLoad : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return true if load is a non-null value, otherwise false.
|
||||||
|
*/
|
||||||
|
public boolean isLoadDefined() {
|
||||||
|
return (m_currentLoad != null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHighLoadWatermark(Integer wm) {
|
||||||
|
m_highLoadWatermark = wm;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOverloadThreshold (int t) {
|
||||||
|
m_overloadThreshold = t;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setShowyOnlyIfOverload (Boolean o) {
|
||||||
|
m_showOnlyIfOverload = o;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setParentBgColor(Color c) {
|
||||||
|
m_parentBgColor = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- paint methods ---
|
||||||
|
|
||||||
|
/** get a color that corresponds to the current load */
|
||||||
|
private Color getLoadColor() {
|
||||||
|
|
||||||
|
if (getLoad() < m_overloadThreshold) {
|
||||||
|
return IMulticoreVisualizerConstants.COLOR_LOAD_LOADBAR_NORMAL;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return IMulticoreVisualizerConstants.COLOR_LOAD_LOADBAR_OVERLOAD;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Invoked to allow element to paint itself on the viewer canvas */
|
||||||
|
@Override
|
||||||
|
public void paintContent(GC gc) {
|
||||||
|
if (!m_enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getLoad() < m_overloadThreshold && m_showOnlyIfOverload)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Show meter only if there is enough space
|
||||||
|
if (m_bounds.height < 30) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_parentBgColor == null) {
|
||||||
|
// use default bg color
|
||||||
|
m_parentBgColor = IMulticoreVisualizerConstants.COLOR_LOAD_UNDERBAR_BG_DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Display complete-length load bar
|
||||||
|
gc.setForeground(IMulticoreVisualizerConstants.COLOR_LOAD_UNDERBAR_FG);
|
||||||
|
gc.setBackground(m_parentBgColor);
|
||||||
|
gc.fillRectangle(m_bounds);
|
||||||
|
gc.drawRectangle(m_bounds);
|
||||||
|
|
||||||
|
// Create/display shorter bar over to show current load
|
||||||
|
int x,y,w,h;
|
||||||
|
x = m_bounds.x;
|
||||||
|
y = (int) (m_bounds.y + m_bounds.height * ((100.0f - getLoad()) / 100.0f));
|
||||||
|
w = m_bounds.width;
|
||||||
|
h = (int) (m_bounds.height - m_bounds.height * ((100.0f - getLoad()) / 100.0f));
|
||||||
|
|
||||||
|
m_loadRect = new Rectangle(x, y, w, h);
|
||||||
|
gc.setBackground(getLoadColor());
|
||||||
|
gc.fillRectangle(m_loadRect);
|
||||||
|
gc.drawRectangle(m_loadRect);
|
||||||
|
|
||||||
|
// Display high water-mark, if defined
|
||||||
|
if ( m_highLoadWatermark != null) {
|
||||||
|
x = m_bounds.x - 5;
|
||||||
|
y = (int) (m_bounds.y + m_bounds.height * ((100.0f - m_highLoadWatermark) / 100.0f));
|
||||||
|
w = m_bounds.width + 7;
|
||||||
|
h = 2;
|
||||||
|
|
||||||
|
m_highWatermarkRect = new Rectangle(x, y, w, h);
|
||||||
|
gc.setBackground(Colors.BLACK);
|
||||||
|
gc.setForeground(Colors.DARK_RED);
|
||||||
|
gc.fillRectangle(m_highWatermarkRect);
|
||||||
|
gc.drawRectangle(m_highWatermarkRect);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns true if object has decorations to paint. */
|
||||||
|
@Override
|
||||||
|
public boolean hasDecorations() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Invoked to allow element to paint decorations on top of anything drawn on it */
|
||||||
|
@Override
|
||||||
|
public void paintDecorations(GC gc) {
|
||||||
|
String load;
|
||||||
|
|
||||||
|
// display nothing if load meter is not enabled
|
||||||
|
if (!m_enabled)
|
||||||
|
return;
|
||||||
|
// "display only if overload" mode applicable?
|
||||||
|
if (getLoad() < m_overloadThreshold && m_showOnlyIfOverload)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// is there an actual value to display yet?
|
||||||
|
if (isLoadDefined()) {
|
||||||
|
load = Integer.toString(getLoad());
|
||||||
|
}
|
||||||
|
// no
|
||||||
|
else {
|
||||||
|
load = "n/a"; //$NON-NLS-1$
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show load text only if there is enough space
|
||||||
|
if (m_bounds.height > 50) {
|
||||||
|
// Display load in text above the load monitor bar
|
||||||
|
gc.setForeground(IMulticoreVisualizerConstants.COLOR_LOAD_TEXT);
|
||||||
|
int tx = m_bounds.x;
|
||||||
|
int ty = m_bounds.y;
|
||||||
|
GUIUtils.drawTextAligned(gc, load, tx, ty, true, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -7,6 +7,7 @@
|
||||||
*
|
*
|
||||||
* Contributors:
|
* Contributors:
|
||||||
* William R. Swanson (Tilera Corporation) - initial API and implementation
|
* William R. Swanson (Tilera Corporation) - initial API and implementation
|
||||||
|
* Marc Dumais (Ericsson) - Add CPU/core load information to the multicore visualizer (Bug 396268)
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
|
||||||
package org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.utils;
|
package org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.utils;
|
||||||
|
@ -14,6 +15,7 @@ package org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.utils;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
import org.eclipse.cdt.dsf.concurrent.ConfinedToDsfExecutor;
|
import org.eclipse.cdt.dsf.concurrent.ConfinedToDsfExecutor;
|
||||||
|
import org.eclipse.cdt.dsf.concurrent.DsfExecutor;
|
||||||
import org.eclipse.cdt.dsf.concurrent.ImmediateCountingRequestMonitor;
|
import org.eclipse.cdt.dsf.concurrent.ImmediateCountingRequestMonitor;
|
||||||
import org.eclipse.cdt.dsf.concurrent.ImmediateDataRequestMonitor;
|
import org.eclipse.cdt.dsf.concurrent.ImmediateDataRequestMonitor;
|
||||||
import org.eclipse.cdt.dsf.concurrent.ImmediateRequestMonitor;
|
import org.eclipse.cdt.dsf.concurrent.ImmediateRequestMonitor;
|
||||||
|
@ -34,8 +36,12 @@ import org.eclipse.cdt.dsf.gdb.service.IGDBHardwareAndOS;
|
||||||
import org.eclipse.cdt.dsf.gdb.service.IGDBHardwareAndOS.ICPUDMContext;
|
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.IGDBHardwareAndOS.ICoreDMContext;
|
||||||
import org.eclipse.cdt.dsf.gdb.service.IGDBHardwareAndOS.IHardwareTargetDMContext;
|
import org.eclipse.cdt.dsf.gdb.service.IGDBHardwareAndOS.IHardwareTargetDMContext;
|
||||||
|
import org.eclipse.cdt.dsf.gdb.service.IGDBHardwareAndOS2;
|
||||||
|
import org.eclipse.cdt.dsf.gdb.service.IGDBHardwareAndOS2.ILoadInfo;
|
||||||
import org.eclipse.cdt.dsf.gdb.service.IGDBProcesses.IGdbThreadDMData;
|
import org.eclipse.cdt.dsf.gdb.service.IGDBProcesses.IGdbThreadDMData;
|
||||||
import org.eclipse.cdt.dsf.mi.service.IMIExecutionDMContext;
|
import org.eclipse.cdt.dsf.mi.service.IMIExecutionDMContext;
|
||||||
|
import org.eclipse.cdt.dsf.service.DsfSession;
|
||||||
|
import org.eclipse.cdt.visualizer.ui.util.Timer;
|
||||||
|
|
||||||
|
|
||||||
/** Debugger state information accessors.
|
/** Debugger state information accessors.
|
||||||
|
@ -81,6 +87,31 @@ public class DSFDebugModel {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Request load information for a single CPU or core
|
||||||
|
* @since 1.1*/
|
||||||
|
@ConfinedToDsfExecutor("getSession().getExecutor()")
|
||||||
|
public static void getLoad(DSFSessionState sessionState,
|
||||||
|
final IDMContext context,
|
||||||
|
final DSFDebugModelListener listener,
|
||||||
|
final Object arg)
|
||||||
|
{
|
||||||
|
IGDBHardwareAndOS2 hwService = sessionState.getService(IGDBHardwareAndOS2.class);
|
||||||
|
if (hwService == null) {
|
||||||
|
listener.getLoadDone(context, null, arg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
hwService.getLoadInfo(context,
|
||||||
|
new ImmediateDataRequestMonitor<ILoadInfo>() {
|
||||||
|
@Override
|
||||||
|
protected void handleCompleted() {
|
||||||
|
listener.getLoadDone(context, getData(), arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/** Requests list of Cores.
|
/** Requests list of Cores.
|
||||||
* Calls back to getCoresDone() on listener. */
|
* Calls back to getCoresDone() on listener. */
|
||||||
@ConfinedToDsfExecutor("getSession().getExecutor()")
|
@ConfinedToDsfExecutor("getSession().getExecutor()")
|
||||||
|
@ -337,4 +368,36 @@ public class DSFDebugModel {
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates and returns a timer that refreshes the load meters
|
||||||
|
* @since 1.1
|
||||||
|
*/
|
||||||
|
public static Timer getLoadTimer(final DSFSessionState sessionState,
|
||||||
|
final int timeout,
|
||||||
|
final DSFDebugModelListener listener)
|
||||||
|
{
|
||||||
|
|
||||||
|
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() {
|
||||||
|
listener.updateLoads();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return t;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
*
|
*
|
||||||
* Contributors:
|
* Contributors:
|
||||||
* William R. Swanson (Tilera Corporation) - initial API and implementation
|
* William R. Swanson (Tilera Corporation) - initial API and implementation
|
||||||
|
* Marc Dumais (Ericsson) - Add CPU/core load information to the multicore visualizer (Bug 396268)
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
|
||||||
package org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.utils;
|
package org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.utils;
|
||||||
|
@ -17,6 +18,7 @@ import org.eclipse.cdt.dsf.debug.service.IProcesses.IThreadDMData;
|
||||||
import org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.ui.model.VisualizerExecutionState;
|
import org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.ui.model.VisualizerExecutionState;
|
||||||
import org.eclipse.cdt.dsf.gdb.service.IGDBHardwareAndOS.ICPUDMContext;
|
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.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.IMIExecutionDMContext;
|
||||||
|
|
||||||
/** Interface for classes that interact with DSFDebugModel.
|
/** Interface for classes that interact with DSFDebugModel.
|
||||||
|
@ -64,5 +66,17 @@ public interface DSFDebugModelListener {
|
||||||
IThreadDMData threadData,
|
IThreadDMData threadData,
|
||||||
VisualizerExecutionState state,
|
VisualizerExecutionState state,
|
||||||
Object arg);
|
Object arg);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoked when getLoad() request completes.
|
||||||
|
* @since 1.1
|
||||||
|
*/
|
||||||
|
public void getLoadDone(IDMContext context, ILoadInfo loads, Object arg);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoked when the load timer triggers
|
||||||
|
* @since 1.1
|
||||||
|
*/
|
||||||
|
public void updateLoads();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2013 Ericsson
|
||||||
|
* 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:
|
||||||
|
* Marc Dumais (Ericsson) - Initial API and implementation (Bug 396268)
|
||||||
|
*******************************************************************************/
|
||||||
|
package org.eclipse.cdt.dsf.gdb.internal;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class provides a container to store the computed
|
||||||
|
* loads for the various CPU cores.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class ProcStatCoreLoads {
|
||||||
|
private Map<String, Float> m_coreLoads;
|
||||||
|
|
||||||
|
public ProcStatCoreLoads() {
|
||||||
|
m_coreLoads = new HashMap<String,Float>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void put(String coreId, Float load) {
|
||||||
|
m_coreLoads.put(coreId,load);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param cpuId: the cpu/core id, as listed in /proc/cpuinfo.
|
||||||
|
* For example, for the core labelled "cpu0" in /proc/stat,
|
||||||
|
* use id "0".
|
||||||
|
* @return The measured load for that core
|
||||||
|
*/
|
||||||
|
public Float getLoad(String cpuId) {
|
||||||
|
return m_coreLoads.get("cpu"+cpuId); //$NON-NLS-1$
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,140 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2013 Ericsson
|
||||||
|
* 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:
|
||||||
|
* Marc Dumais (Ericsson) - Initial API and implementation (Bug 396268)
|
||||||
|
*******************************************************************************/
|
||||||
|
|
||||||
|
package org.eclipse.cdt.dsf.gdb.internal;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class that holds one set of /proc/stat counters.
|
||||||
|
* TODO: extend to more than the tick counters.
|
||||||
|
*/
|
||||||
|
public class ProcStatCounters {
|
||||||
|
private Map<String,OneCoreTickCounters> fTickCounters = new HashMap<String,OneCoreTickCounters>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An object of this class holds one set of core/CPU tick counter values, for a single CPU core
|
||||||
|
*/
|
||||||
|
private class OneCoreTickCounters {
|
||||||
|
private int fUser;
|
||||||
|
private int fNice;
|
||||||
|
private int fSystem;
|
||||||
|
private int fIdle;
|
||||||
|
private int fIowait;
|
||||||
|
private int fIrq;
|
||||||
|
private int fSoftirq;
|
||||||
|
|
||||||
|
public OneCoreTickCounters(Integer[] c) {
|
||||||
|
// sanity checks
|
||||||
|
assert (c != null && c.length >= 7);
|
||||||
|
if (c == null || c.length < 7) return;
|
||||||
|
|
||||||
|
fUser = c[0];
|
||||||
|
fNice = c[1];
|
||||||
|
fSystem = c[2];
|
||||||
|
fIdle = c[3];
|
||||||
|
fIowait = c[4];
|
||||||
|
fIrq = c[5];
|
||||||
|
fSoftirq = c[6];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The sum of all "active" (i.e. non-idle) tick counters
|
||||||
|
*/
|
||||||
|
private int getActiveTicks() {
|
||||||
|
return fUser + fNice + fSystem + fIowait + fIrq + fSoftirq;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The "idle" tick counter
|
||||||
|
*/
|
||||||
|
private int getIdleTicks() {
|
||||||
|
return fIdle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public ProcStatCounters() {
|
||||||
|
fTickCounters = new HashMap<String,OneCoreTickCounters>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves the tick counters for one core
|
||||||
|
* @param core: the core id, as seen in /proc/stat.
|
||||||
|
* @param ticks: Array of tick counters, as read from a CPU/core line in /proc/stat
|
||||||
|
*/
|
||||||
|
public void addTickCounters(String core, Integer[] ticks) {
|
||||||
|
fTickCounters.put(core, new OneCoreTickCounters(ticks));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Note: It was discovered during testing that sometimes, the counters in
|
||||||
|
* /proc/stat are not updated for a given core, between two measurements.
|
||||||
|
* The cause seems to be that with CPUs such as the i5 and i7, some power-
|
||||||
|
* saving modes can put a core to sleep for a short time. When all counters
|
||||||
|
* for a core are the same for 2 measurements, it can cause a division by
|
||||||
|
* zero below, in the load computing code. Given that this can legitimately
|
||||||
|
* happen, we handle the case and assign a load of zero, when it does.
|
||||||
|
*
|
||||||
|
* @param old: another ProcStatCounters object. If null, will compute the
|
||||||
|
* average load from boot time (i.e. historical load).
|
||||||
|
* @return the load, for each CPU core, computed from the two
|
||||||
|
* sets of counters.
|
||||||
|
*/
|
||||||
|
public final ProcStatCoreLoads computeLoads(final ProcStatCounters old) {
|
||||||
|
ProcStatCoreLoads loads = new ProcStatCoreLoads();
|
||||||
|
|
||||||
|
// for each core
|
||||||
|
for(String coreId: fTickCounters.keySet()) {
|
||||||
|
OneCoreTickCounters coreCountersNew = fTickCounters.get(coreId);
|
||||||
|
// Do we have 2 sets of counters to compute the load from?
|
||||||
|
if (old != null) {
|
||||||
|
OneCoreTickCounters coreCountersOld = old.fTickCounters.get(coreId);
|
||||||
|
int diffIdle = coreCountersNew.getIdleTicks() - coreCountersOld.getIdleTicks();
|
||||||
|
int diffActive = coreCountersNew.getActiveTicks() - coreCountersOld.getActiveTicks();
|
||||||
|
|
||||||
|
// Sanity check - we do not expect that the counter should decrease
|
||||||
|
assert(diffIdle >= 0);
|
||||||
|
assert(diffActive >= 0);
|
||||||
|
|
||||||
|
if (diffIdle < 0 || diffActive < 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
float load;
|
||||||
|
if (diffIdle + diffActive != 0) {
|
||||||
|
load = diffActive / (float)(diffActive + diffIdle);
|
||||||
|
}
|
||||||
|
// Here we catch the cases where a core has been asleep for the whole
|
||||||
|
// measurement period. See note above this method.
|
||||||
|
else {
|
||||||
|
load = 0;
|
||||||
|
}
|
||||||
|
loads.put(coreId, load * 100.0f);
|
||||||
|
}
|
||||||
|
// we have only one set of counters; we will effectively compute the historical load,
|
||||||
|
// from boot time until now.
|
||||||
|
else {
|
||||||
|
int diffIdle = coreCountersNew.getIdleTicks();
|
||||||
|
int diffActive = coreCountersNew.getActiveTicks();
|
||||||
|
assert (diffActive + diffIdle != 0);
|
||||||
|
float load = diffActive / (float)(diffActive + diffIdle);
|
||||||
|
loads.put(coreId, load * 100.0f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return loads;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,100 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2013 Ericsson
|
||||||
|
* 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:
|
||||||
|
* Marc Dumais (Ericsson) - Initial API and implementation (Bug 396268)
|
||||||
|
*******************************************************************************/
|
||||||
|
package org.eclipse.cdt.dsf.gdb.internal;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.io.Reader;
|
||||||
|
import java.util.Vector;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Marc Dumais
|
||||||
|
* TODO: extend to more than the tick counters.
|
||||||
|
* @see also http://www.linuxhowtos.org/System/procstat.htm
|
||||||
|
*/
|
||||||
|
public class ProcStatParser {
|
||||||
|
|
||||||
|
private ProcStatCounters cpuCoreCounters;
|
||||||
|
private ProcStatCounters cpuCoreCountersOld;
|
||||||
|
|
||||||
|
public ProcStatParser() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read and parse the /proc/stat file given as param
|
||||||
|
* @param fileName
|
||||||
|
*/
|
||||||
|
public void parseStatFile(String fileName) throws FileNotFoundException, NumberFormatException {
|
||||||
|
cpuCoreCountersOld = cpuCoreCounters;
|
||||||
|
|
||||||
|
File statFile = new File(fileName);
|
||||||
|
if (!statFile.exists()) {
|
||||||
|
throw new FileNotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
cpuCoreCounters = new ProcStatCounters();
|
||||||
|
BufferedReader reader = null;
|
||||||
|
try {
|
||||||
|
String coreId;
|
||||||
|
Reader r = new InputStreamReader(new FileInputStream(statFile));
|
||||||
|
reader = new BufferedReader(r);
|
||||||
|
String line;
|
||||||
|
// ex: "cpu0 2048635 3195 385292 66149962 895977 22 36130 0 0 0"
|
||||||
|
// note: we intentionally do not catch the "cpu" (without a core number) line.
|
||||||
|
Pattern patternCpu = Pattern.compile("^(cpu[0-9]+)(.*)$"); //$NON-NLS-1$
|
||||||
|
|
||||||
|
while ((line = reader.readLine()) != null) {
|
||||||
|
line = line.trim();
|
||||||
|
|
||||||
|
// catch "cpu" lines from /proc/stat
|
||||||
|
Matcher matcherCpu = patternCpu.matcher(line);
|
||||||
|
if (matcherCpu.find()) {
|
||||||
|
Vector<Integer> ticks = new Vector<Integer>();
|
||||||
|
coreId = matcherCpu.group(1);
|
||||||
|
// extract the counters for current cpu line
|
||||||
|
for (String tick : matcherCpu.group(2).trim().split("\\s+")) { //$NON-NLS-1$
|
||||||
|
ticks.add(Integer.parseInt(tick));
|
||||||
|
}
|
||||||
|
|
||||||
|
cpuCoreCounters.addTickCounters(coreId, ticks.toArray(new Integer[ticks.size()]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (IOException e) {
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
if (reader != null) {
|
||||||
|
reader.close();
|
||||||
|
}
|
||||||
|
} catch (IOException e) {/* Don't care */}
|
||||||
|
reader = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return a Map of the computed CPU/core loads. The load of individual
|
||||||
|
* CPUs/cores can be found with keys "cpuN", where N is the CPU/core
|
||||||
|
* number, starting with 0, as found in /proc/stat .
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public ProcStatCoreLoads getCpuLoad() {
|
||||||
|
return cpuCoreCounters.computeLoads(cpuCoreCountersOld);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* Copyright (c) 2012 Ericsson and others.
|
* Copyright (c) 2012-2013 Ericsson and others.
|
||||||
* All rights reserved. This program and the accompanying materials
|
* All rights reserved. This program and the accompanying materials
|
||||||
* are made available under the terms of the Eclipse Public License v1.0
|
* are made available under the terms of the Eclipse Public License v1.0
|
||||||
* which accompanies this distribution, and is available at
|
* which accompanies this distribution, and is available at
|
||||||
|
@ -8,17 +8,22 @@
|
||||||
* Contributors:
|
* Contributors:
|
||||||
* Marc Khouzam (Ericsson) - initial API and implementation
|
* Marc Khouzam (Ericsson) - initial API and implementation
|
||||||
* Marc Khouzam (Ericsson) - Updated to use /proc/cpuinfo for remote targets (Bug 374024)
|
* Marc Khouzam (Ericsson) - Updated to use /proc/cpuinfo for remote targets (Bug 374024)
|
||||||
|
* Marc Dumais (Ericsson) - Add CPU/core load information to the multicore visualizer (Bug 396268)
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
package org.eclipse.cdt.dsf.gdb.service;
|
package org.eclipse.cdt.dsf.gdb.service;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Hashtable;
|
import java.util.Hashtable;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.Vector;
|
import java.util.Vector;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
|
import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
|
||||||
import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants;
|
import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants;
|
||||||
|
@ -27,6 +32,7 @@ import org.eclipse.cdt.dsf.concurrent.ImmediateExecutor;
|
||||||
import org.eclipse.cdt.dsf.concurrent.Immutable;
|
import org.eclipse.cdt.dsf.concurrent.Immutable;
|
||||||
import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
|
import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
|
||||||
import org.eclipse.cdt.dsf.datamodel.AbstractDMContext;
|
import org.eclipse.cdt.dsf.datamodel.AbstractDMContext;
|
||||||
|
import org.eclipse.cdt.dsf.datamodel.DMContexts;
|
||||||
import org.eclipse.cdt.dsf.datamodel.DataModelInitializedEvent;
|
import org.eclipse.cdt.dsf.datamodel.DataModelInitializedEvent;
|
||||||
import org.eclipse.cdt.dsf.datamodel.IDMContext;
|
import org.eclipse.cdt.dsf.datamodel.IDMContext;
|
||||||
import org.eclipse.cdt.dsf.datamodel.IDMData;
|
import org.eclipse.cdt.dsf.datamodel.IDMData;
|
||||||
|
@ -42,6 +48,8 @@ import org.eclipse.cdt.dsf.debug.service.command.IEventListener;
|
||||||
import org.eclipse.cdt.dsf.gdb.internal.CoreInfo;
|
import org.eclipse.cdt.dsf.gdb.internal.CoreInfo;
|
||||||
import org.eclipse.cdt.dsf.gdb.internal.CoreList;
|
import org.eclipse.cdt.dsf.gdb.internal.CoreList;
|
||||||
import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin;
|
import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin;
|
||||||
|
import org.eclipse.cdt.dsf.gdb.internal.ProcStatCoreLoads;
|
||||||
|
import org.eclipse.cdt.dsf.gdb.internal.ProcStatParser;
|
||||||
import org.eclipse.cdt.dsf.gdb.internal.service.command.commands.MIMetaGetCPUInfo;
|
import org.eclipse.cdt.dsf.gdb.internal.service.command.commands.MIMetaGetCPUInfo;
|
||||||
import org.eclipse.cdt.dsf.gdb.internal.service.command.output.MIMetaGetCPUInfoInfo;
|
import org.eclipse.cdt.dsf.gdb.internal.service.command.output.MIMetaGetCPUInfoInfo;
|
||||||
import org.eclipse.cdt.dsf.gdb.service.command.IGDBControl;
|
import org.eclipse.cdt.dsf.gdb.service.command.IGDBControl;
|
||||||
|
@ -65,7 +73,7 @@ import org.osgi.framework.BundleContext;
|
||||||
*
|
*
|
||||||
* @since 4.1
|
* @since 4.1
|
||||||
*/
|
*/
|
||||||
public class GDBHardwareAndOS extends AbstractDsfService implements IGDBHardwareAndOS, ICachingService {
|
public class GDBHardwareAndOS extends AbstractDsfService implements IGDBHardwareAndOS2, ICachingService {
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
protected static class GDBCPUDMC extends AbstractDMContext
|
protected static class GDBCPUDMC extends AbstractDMContext
|
||||||
|
@ -152,6 +160,34 @@ public class GDBHardwareAndOS extends AbstractDsfService implements IGDBHardware
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 4.2
|
||||||
|
*/
|
||||||
|
@Immutable
|
||||||
|
protected class GDBLoadInfo implements ILoadInfo {
|
||||||
|
private String fLoad;
|
||||||
|
private Map<String,String> fDetailedLoad;
|
||||||
|
|
||||||
|
public GDBLoadInfo(String load, Map<String,String> detailedLoad) {
|
||||||
|
fLoad = load;
|
||||||
|
fDetailedLoad = detailedLoad;
|
||||||
|
}
|
||||||
|
public GDBLoadInfo(String load) {
|
||||||
|
this(load, null);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public String getLoad() {
|
||||||
|
return fLoad;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public Map<String,String> getDetailedLoad() {
|
||||||
|
return fDetailedLoad;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// to save queued load info requests for later processing
|
||||||
|
private Map<IDMContext, DataRequestMonitor<ILoadInfo>> fLoadInfoRequestCache;
|
||||||
|
|
||||||
private IGDBControl fCommandControl;
|
private IGDBControl fCommandControl;
|
||||||
private IGDBBackend fBackend;
|
private IGDBBackend fBackend;
|
||||||
private CommandFactory fCommandFactory;
|
private CommandFactory fCommandFactory;
|
||||||
|
@ -170,6 +206,19 @@ public class GDBHardwareAndOS extends AbstractDsfService implements IGDBHardware
|
||||||
// Bug 374293
|
// Bug 374293
|
||||||
private boolean fSessionInitializationComplete;
|
private boolean fSessionInitializationComplete;
|
||||||
|
|
||||||
|
// used to keep track when we last computed the load
|
||||||
|
private long fLastCpuLoadRefresh = 0;
|
||||||
|
// to keep track if we are already seeking to get the load
|
||||||
|
private boolean fLoadRequestOngoing = false;
|
||||||
|
// Length of the measured sample in ms
|
||||||
|
private final static int LOAD_SAMPLE_DELAY = 250;
|
||||||
|
// To avoid bombarding the remote GDB server, we cache the measured load
|
||||||
|
// and serve it again if requested within a short period of time.
|
||||||
|
private ProcStatCoreLoads fCachedLoads = null;
|
||||||
|
// lifetime of the load cache, in ms
|
||||||
|
private final static int LOAD_CACHE_LIFETIME = 500;
|
||||||
|
|
||||||
|
|
||||||
public GDBHardwareAndOS(DsfSession session) {
|
public GDBHardwareAndOS(DsfSession session) {
|
||||||
super(session);
|
super(session);
|
||||||
}
|
}
|
||||||
|
@ -211,11 +260,13 @@ public class GDBHardwareAndOS extends AbstractDsfService implements IGDBHardware
|
||||||
// handle getting the required cpu info
|
// handle getting the required cpu info
|
||||||
fFetchCPUInfoCache = new CommandCache(getSession(), new CPUInfoManager());
|
fFetchCPUInfoCache = new CommandCache(getSession(), new CPUInfoManager());
|
||||||
fFetchCPUInfoCache.setContextAvailable(fCommandControl.getContext(), true);
|
fFetchCPUInfoCache.setContextAvailable(fCommandControl.getContext(), true);
|
||||||
|
fLoadInfoRequestCache = new HashMap<IDMContext, DataRequestMonitor<ILoadInfo>>();
|
||||||
|
|
||||||
getSession().addServiceEventListener(this, null);
|
getSession().addServiceEventListener(this, null);
|
||||||
|
|
||||||
// Register this service.
|
// Register this service.
|
||||||
register(new String[] { IGDBHardwareAndOS.class.getName(),
|
register(new String[] { IGDBHardwareAndOS.class.getName(),
|
||||||
|
IGDBHardwareAndOS2.class.getName(),
|
||||||
GDBHardwareAndOS.class.getName() },
|
GDBHardwareAndOS.class.getName() },
|
||||||
new Hashtable<String, String>());
|
new Hashtable<String, String>());
|
||||||
|
|
||||||
|
@ -234,6 +285,7 @@ public class GDBHardwareAndOS extends AbstractDsfService implements IGDBHardware
|
||||||
public void shutdown(RequestMonitor requestMonitor) {
|
public void shutdown(RequestMonitor requestMonitor) {
|
||||||
getSession().removeServiceEventListener(this);
|
getSession().removeServiceEventListener(this);
|
||||||
fFetchCPUInfoCache.reset();
|
fFetchCPUInfoCache.reset();
|
||||||
|
fLoadInfoRequestCache.clear();
|
||||||
unregister();
|
unregister();
|
||||||
super.shutdown(requestMonitor);
|
super.shutdown(requestMonitor);
|
||||||
}
|
}
|
||||||
|
@ -502,4 +554,251 @@ public class GDBHardwareAndOS extends AbstractDsfService implements IGDBHardware
|
||||||
public void removeCommand(ICommandToken token) { assert false : "Not supported"; } //$NON-NLS-1$
|
public void removeCommand(ICommandToken token) { assert false : "Not supported"; } //$NON-NLS-1$
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 4.2
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean isAvailable() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 4.2
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void getResourceClasses(IDMContext dmc,
|
||||||
|
DataRequestMonitor<IResourceClass[]> rm) {
|
||||||
|
rm.done(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, NOT_SUPPORTED, "Operation not supported", null)); //$NON-NLS-1$
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 4.2
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void getResourcesInformation(IDMContext dmc, String resourceClassId,
|
||||||
|
DataRequestMonitor<IResourcesInformation> rm) {
|
||||||
|
rm.done(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, NOT_SUPPORTED, "Operation not supported", null)); //$NON-NLS-1$
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method processes "load info" requests. The load is computed using a
|
||||||
|
* sampling method; two readings of a local or remote /proc/stat file are done
|
||||||
|
* with a delay in between. Then the load is computed from the two samples,
|
||||||
|
* for all CPUs/cores known in the system.
|
||||||
|
*
|
||||||
|
* Because of the method used, it's possible that fast variations in CPU usage will
|
||||||
|
* be missed. However longer load trends should be reflected in the results.
|
||||||
|
*
|
||||||
|
* To avoid generating too much load in the remote case, there is a cache that will
|
||||||
|
* return the already computed load, if requested multiple times in a short period.
|
||||||
|
* There is also a mechanism to queue subsequent requests if one is ongoing. Upon
|
||||||
|
* completion of the ongoing request, any queued request is answered with the load
|
||||||
|
* that was just computed.
|
||||||
|
*
|
||||||
|
* @since 4.2
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void getLoadInfo(final IDMContext context, final DataRequestMonitor<ILoadInfo> rm) {
|
||||||
|
if (!(context instanceof ICoreDMContext) && !(context instanceof ICPUDMContext)) {
|
||||||
|
// we only support getting the load for a CPU or a core
|
||||||
|
rm.done(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, "Load information not supported for this context type", null)); //$NON-NLS-1$
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The measurement interval should be of a minimum length to be meaningful
|
||||||
|
assert (LOAD_SAMPLE_DELAY >= 100);
|
||||||
|
// so the cache is useful
|
||||||
|
assert (LOAD_CACHE_LIFETIME >= LOAD_SAMPLE_DELAY);
|
||||||
|
|
||||||
|
// This way of computing the CPU load is only applicable to Linux
|
||||||
|
if (!Platform.getOS().equals(Platform.OS_LINUX)) {
|
||||||
|
rm.done(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, NOT_SUPPORTED, "Operation not supported", null)); //$NON-NLS-1$
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Is a request is already ongoing?
|
||||||
|
if(fLoadRequestOngoing) {
|
||||||
|
// queue current new request
|
||||||
|
fLoadInfoRequestCache.put(context, rm);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// no request ongoing, so proceed
|
||||||
|
fLoadRequestOngoing = true;
|
||||||
|
|
||||||
|
// caching mechanism to keep things sane, even if the views(s)
|
||||||
|
// request load information very often.
|
||||||
|
long currentTime = System.currentTimeMillis();
|
||||||
|
|
||||||
|
// time to fetch fresh load information?
|
||||||
|
if (fLastCpuLoadRefresh + LOAD_CACHE_LIFETIME < currentTime) {
|
||||||
|
fLastCpuLoadRefresh = currentTime;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// not time yet... re-use cached load data
|
||||||
|
processLoads(context, rm, fCachedLoads);
|
||||||
|
fLoadRequestOngoing = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final ProcStatParser procStatParser = new ProcStatParser();
|
||||||
|
final ICommandControlDMContext dmc = DMContexts.getAncestorOfType(context, ICommandControlDMContext.class);
|
||||||
|
final String statFile = "/proc/stat"; //$NON-NLS-1$
|
||||||
|
final String localFile = "/tmp/" + GdbPlugin.PLUGIN_ID + ".proc.stat." + getSession().getId(); //$NON-NLS-1$ //$NON-NLS-2$
|
||||||
|
|
||||||
|
// Remote debugging? We will ask GDB to get us the /proc/stat file from target, twice, with a delay between.
|
||||||
|
if (fBackend.getSessionType() == SessionType.REMOTE) {
|
||||||
|
fCommandControl.queueCommand(
|
||||||
|
fCommandFactory.createCLIRemoteGet(dmc, statFile, localFile),
|
||||||
|
new ImmediateDataRequestMonitor<MIInfo>(rm) {
|
||||||
|
@Override
|
||||||
|
protected void handleCompleted() {
|
||||||
|
if (! isSuccess()) {
|
||||||
|
fLoadRequestOngoing = false;
|
||||||
|
rm.done(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Can't get load info for CPU", null)); //$NON-NLS-1$
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Success - parse first set of stat counters
|
||||||
|
try {
|
||||||
|
procStatParser.parseStatFile(localFile);
|
||||||
|
} catch (Exception e) {
|
||||||
|
rm.done(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Can't get load info for CPU", null)); //$NON-NLS-1$
|
||||||
|
fLoadRequestOngoing = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// delete temp file
|
||||||
|
new File(localFile).delete();
|
||||||
|
|
||||||
|
getExecutor().schedule(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
fCommandControl.queueCommand(
|
||||||
|
fCommandFactory.createCLIRemoteGet(dmc, statFile, localFile),
|
||||||
|
new ImmediateDataRequestMonitor<MIInfo>(rm) {
|
||||||
|
@Override
|
||||||
|
protected void handleCompleted() {
|
||||||
|
if (! isSuccess()) {
|
||||||
|
fLoadRequestOngoing = false;
|
||||||
|
rm.done(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Can't get load info for CPU", null)); //$NON-NLS-1$
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Success - parse the second set of stat counters and compute loads
|
||||||
|
try {
|
||||||
|
procStatParser.parseStatFile(localFile);
|
||||||
|
} catch (Exception e) {
|
||||||
|
rm.done(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Can't get load info for CPU", null)); //$NON-NLS-1$
|
||||||
|
fLoadRequestOngoing = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// delete temp file
|
||||||
|
new File(localFile).delete();
|
||||||
|
|
||||||
|
// Compute load
|
||||||
|
fCachedLoads = procStatParser.getCpuLoad();
|
||||||
|
processLoads(context, rm, fCachedLoads);
|
||||||
|
|
||||||
|
// done with request
|
||||||
|
fLoadRequestOngoing = false;
|
||||||
|
// process any queued request
|
||||||
|
for(Entry<IDMContext, DataRequestMonitor<ILoadInfo>> e : fLoadInfoRequestCache.entrySet()) {
|
||||||
|
processLoads(e.getKey(), e.getValue(), fCachedLoads);
|
||||||
|
}
|
||||||
|
fLoadInfoRequestCache.clear();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, LOAD_SAMPLE_DELAY, TimeUnit.MILLISECONDS);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// Local debugging? Then we can read /proc/stat directly
|
||||||
|
} else {
|
||||||
|
// Read /proc/stat file for the first time
|
||||||
|
try {
|
||||||
|
procStatParser.parseStatFile(statFile);
|
||||||
|
} catch (Exception e) {
|
||||||
|
rm.done(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Can't get load info for CPU", null)); //$NON-NLS-1$
|
||||||
|
fLoadRequestOngoing = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read /proc/stat file again after a delay
|
||||||
|
getExecutor().schedule(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
procStatParser.parseStatFile(statFile);
|
||||||
|
} catch (Exception e) {
|
||||||
|
rm.done(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Can't get load info for CPU", null)); //$NON-NLS-1$
|
||||||
|
fLoadRequestOngoing = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// compute load
|
||||||
|
fCachedLoads = procStatParser.getCpuLoad();
|
||||||
|
processLoads(context, rm, fCachedLoads);
|
||||||
|
|
||||||
|
// done with request
|
||||||
|
fLoadRequestOngoing = false;
|
||||||
|
// process any queued request
|
||||||
|
for(Entry<IDMContext, DataRequestMonitor<ILoadInfo>> e : fLoadInfoRequestCache.entrySet()) {
|
||||||
|
processLoads(e.getKey(), e.getValue(), fCachedLoads);
|
||||||
|
}
|
||||||
|
fLoadInfoRequestCache.clear();
|
||||||
|
}
|
||||||
|
}, LOAD_SAMPLE_DELAY, TimeUnit.MILLISECONDS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For a given "load info" request, this method processes the load obtained from the
|
||||||
|
* proc stat parser and creates/sends the response.
|
||||||
|
* @param context
|
||||||
|
* @param rm
|
||||||
|
* @param loads
|
||||||
|
*/
|
||||||
|
private void processLoads(final IDMContext context, final DataRequestMonitor<ILoadInfo> rm, final ProcStatCoreLoads loads) {
|
||||||
|
|
||||||
|
// problem with fetching load info
|
||||||
|
if (loads == null) {
|
||||||
|
rm.done(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Can't get load info", null)); //$NON-NLS-1$
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// core context?
|
||||||
|
if (context instanceof ICoreDMContext) {
|
||||||
|
String coreId = ((ICoreDMContext) context).getId();
|
||||||
|
// Integer precision sufficient for our purpose
|
||||||
|
float load = loads.getLoad(coreId);
|
||||||
|
rm.done(new GDBLoadInfo(Integer.toString((int)load)));
|
||||||
|
}
|
||||||
|
else if (context instanceof ICPUDMContext) {
|
||||||
|
// get the list of cores in that CPU
|
||||||
|
getCores(context,
|
||||||
|
new ImmediateDataRequestMonitor<ICoreDMContext[]>() {
|
||||||
|
@Override
|
||||||
|
protected void handleCompleted() {
|
||||||
|
ICoreDMContext[] coreContexts = getData();
|
||||||
|
|
||||||
|
if (!isSuccess() || coreContexts == null || coreContexts.length < 1) {
|
||||||
|
// Unable to get any core data
|
||||||
|
rm.done(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Can't get load info for CPU", null)); //$NON-NLS-1$
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
float load = 0.0f;
|
||||||
|
// compute the average load of cores in that CPU
|
||||||
|
for (ICoreDMContext coreCtx : coreContexts) {
|
||||||
|
String coreId = coreCtx.getId();
|
||||||
|
load += loads.getLoad(coreId);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
load /= i;
|
||||||
|
rm.done(new GDBLoadInfo(Integer.toString((int)load)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,10 +7,13 @@
|
||||||
*
|
*
|
||||||
* Contributors:
|
* Contributors:
|
||||||
* Vladimir Prus (Mentor Graphics) - initial API and implementation
|
* Vladimir Prus (Mentor Graphics) - initial API and implementation
|
||||||
|
* Marc Dumais (Ericsson) - Add CPU/core load information to the multicore visualizer (Bug 396268)
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
|
||||||
package org.eclipse.cdt.dsf.gdb.service;
|
package org.eclipse.cdt.dsf.gdb.service;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
|
import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
|
||||||
import org.eclipse.cdt.dsf.datamodel.IDMContext;
|
import org.eclipse.cdt.dsf.datamodel.IDMContext;
|
||||||
|
|
||||||
|
@ -64,4 +67,28 @@ public interface IGDBHardwareAndOS2 extends IGDBHardwareAndOS {
|
||||||
* Return table describing resources of specified class.
|
* Return table describing resources of specified class.
|
||||||
*/
|
*/
|
||||||
void getResourcesInformation(IDMContext dmc, String resourceClassId, DataRequestMonitor<IResourcesInformation> rm);
|
void getResourcesInformation(IDMContext dmc, String resourceClassId, DataRequestMonitor<IResourcesInformation> rm);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Information about the CPU/core load for one given CPU or core
|
||||||
|
*/
|
||||||
|
public interface ILoadInfo
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* A string representing the current load (between "0" and "100")
|
||||||
|
*/
|
||||||
|
public String getLoad();
|
||||||
|
/**
|
||||||
|
* Used to give more details about a CPU's/core's load. For instance
|
||||||
|
* the breakdown of the different load types and their proportion: system,
|
||||||
|
* user, I/O, interrupts, etc.
|
||||||
|
*/
|
||||||
|
public Map<String,String> getDetailedLoad();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes CPU/core load information according to context and
|
||||||
|
* asynchronously returns the result in a ILoadInfo object
|
||||||
|
*/
|
||||||
|
void getLoadInfo(IDMContext dmc, DataRequestMonitor<ILoadInfo> rm);
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
*
|
*
|
||||||
* Contributors:
|
* Contributors:
|
||||||
* Ericsson - Initial Implementation
|
* Ericsson - Initial Implementation
|
||||||
|
* Marc Dumais (Ericsson) - Initial API and implementation (Bug 396268)
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
package org.eclipse.cdt.dsf.mi.service.command.commands;
|
package org.eclipse.cdt.dsf.mi.service.command.commands;
|
||||||
|
|
||||||
|
@ -14,6 +15,7 @@ import org.eclipse.cdt.dsf.mi.service.command.output.MIStringHandlerTests;
|
||||||
import org.eclipse.cdt.dsf.mi.service.command.output.MIThreadTests;
|
import org.eclipse.cdt.dsf.mi.service.command.output.MIThreadTests;
|
||||||
import org.eclipse.cdt.tests.dsf.gdb.framework.OnceOnlySuite;
|
import org.eclipse.cdt.tests.dsf.gdb.framework.OnceOnlySuite;
|
||||||
import org.eclipse.cdt.tests.dsf.gdb.tests.LaunchUtilsTest;
|
import org.eclipse.cdt.tests.dsf.gdb.tests.LaunchUtilsTest;
|
||||||
|
import org.eclipse.cdt.tests.dsf.gdb.tests.ProcStatParserTest;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.junit.runners.Suite;
|
import org.junit.runners.Suite;
|
||||||
|
|
||||||
|
@ -29,7 +31,8 @@ import org.junit.runners.Suite;
|
||||||
TestMICommandConstructCommand.class,
|
TestMICommandConstructCommand.class,
|
||||||
MIThreadTests.class,
|
MIThreadTests.class,
|
||||||
LaunchUtilsTest.class,
|
LaunchUtilsTest.class,
|
||||||
MIStringHandlerTests.class
|
MIStringHandlerTests.class,
|
||||||
|
ProcStatParserTest.class
|
||||||
/* Add your test class here */
|
/* Add your test class here */
|
||||||
})
|
})
|
||||||
public class Suite_Sessionless_Tests {
|
public class Suite_Sessionless_Tests {
|
||||||
|
|
|
@ -0,0 +1,220 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2013 Ericsson
|
||||||
|
* 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:
|
||||||
|
* Marc Dumais (Ericsson) - Initial API and implementation (Bug 396268)
|
||||||
|
*******************************************************************************/
|
||||||
|
package org.eclipse.cdt.tests.dsf.gdb.tests;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.FileWriter;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import org.eclipse.cdt.dsf.gdb.internal.ProcStatCoreLoads;
|
||||||
|
import org.eclipse.cdt.dsf.gdb.internal.ProcStatParser;
|
||||||
|
import org.junit.AfterClass;
|
||||||
|
import org.junit.BeforeClass;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class ProcStatParserTest {
|
||||||
|
|
||||||
|
final static String stat_t0 = "cpu 27599070 16857 1627173 178832624 958471 10 21253 0 0 0\n" +
|
||||||
|
"cpu0 7076626 3073 420740 44122942 620655 7 19123 0 0 0\n" +
|
||||||
|
"cpu1 6839475 2644 480003 44885633 53738 2 1200 0 0 0\n" +
|
||||||
|
"cpu2 6861775 9347 337505 44860715 195008 0 573 0 0 0\n" +
|
||||||
|
"cpu3 6821192 1792 388924 44963332 89067 0 355 0 0 0\n" +
|
||||||
|
"intr 255054962 1785 9 0 0 0 0 0 0 1 393 0 0 125 0 0 0 1861780 5056689 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3138534 3946219 2295808 199 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n" +
|
||||||
|
"ctxt 406954066\n" +
|
||||||
|
"btime 1357642511\n" +
|
||||||
|
"processes 187587\n" +
|
||||||
|
"procs_running 2\n" +
|
||||||
|
"procs_blocked 0\n" +
|
||||||
|
"softirq 187777133 0 82842161 104536 3977894 3827626 0 3881246 12353598 94844 80695228";
|
||||||
|
final static String stat_t0_file = "/tmp/stat_t0";
|
||||||
|
|
||||||
|
final static String stat_t1 = "cpu 27599216 16857 1627190 178835528 958483 10 21255 0 0 0\n" +
|
||||||
|
"cpu0 7076664 3073 420751 44123650 620668 7 19125 0 0 0\n" +
|
||||||
|
"cpu1 6839509 2644 480004 44886368 53738 2 1200 0 0 0\n" +
|
||||||
|
"cpu2 6861813 9347 337507 44861445 195008 0 573 0 0 0\n" +
|
||||||
|
"cpu3 6821229 1792 388926 44964063 89067 0 355 0 0 0\n" +
|
||||||
|
"intr 255057230 1785 9 0 0 0 0 0 0 1 393 0 0 125 0 0 0 1861874 5056997 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3138618 3946264 2295808 199 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n" +
|
||||||
|
"ctxt 406958462\n" +
|
||||||
|
"btime 1357642511\n" +
|
||||||
|
"processes 187593\n" +
|
||||||
|
"procs_running 5\n" +
|
||||||
|
"procs_blocked 0\n" +
|
||||||
|
"softirq 187779126 0 82842674 104538 3977978 3827690 0 3881346 12353760 94845 80696295";
|
||||||
|
final static String stat_t1_file = "/tmp/stat_t1";
|
||||||
|
|
||||||
|
final static String stat_t2 = "cpu 27602962 16857 1627282 178835528 958483 10 21256 0 0 0\n" +
|
||||||
|
"cpu0 7077593 3073 420781 44123650 620668 7 19126 0 0 0\n" +
|
||||||
|
"cpu1 6840413 2644 480060 44886368 53738 2 1200 0 0 0\n" +
|
||||||
|
"cpu2 6862773 9347 337507 44861445 195008 0 573 0 0 0\n" +
|
||||||
|
"cpu3 6822181 1792 388933 44964063 89067 0 355 0 0 0\n" +
|
||||||
|
"intr 255070028 1785 9 0 0 0 0 0 0 1 393 0 0 125 0 0 0 1861998 5057533 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3138674 3946472 2295808 199 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n" +
|
||||||
|
"ctxt 407001757\n" +
|
||||||
|
"btime 1357642511\n" +
|
||||||
|
"processes 187607\n" +
|
||||||
|
"procs_running 5\n" +
|
||||||
|
"procs_blocked 0\n" +
|
||||||
|
"softirq 187794229 0 82852274 104540 3978034 3827918 0 3881474 12354181 94845 80700963";
|
||||||
|
final static String stat_t2_file = "/tmp/stat_t2";
|
||||||
|
|
||||||
|
// to trigger exception upon parsing
|
||||||
|
final static String stat_wrong_content = "cpu 27602962 16857 1627282 178835528 958483 10 21256 0 0 0\n" +
|
||||||
|
"cpu0 AAAAAAA 3073 420781 44123650 620668 7 19126 0 0 0\n" +
|
||||||
|
"cpu1 AAAAAAA 2644 480060 44886368 53738 2 1200 0 0 0\n" +
|
||||||
|
"cpu2 AAAAAAA 9347 337507 44861445 195008 0 573 0 0 0\n" +
|
||||||
|
"cpu3 AAAAAAA 1792 388933 44964063 89067 0 355 0 0 0\n" +
|
||||||
|
"intr 255070028 1785 9 0 0 0 0 0 0 1 393 0 0 125 0 0 0 1861998 5057533 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3138674 3946472 2295808 199 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n" +
|
||||||
|
"ctxt 407001757\n" +
|
||||||
|
"btime 1357642511\n" +
|
||||||
|
"processes 187607\n" +
|
||||||
|
"procs_running 5\n" +
|
||||||
|
"procs_blocked 0\n" +
|
||||||
|
"softirq 187794229 0 82852274 104540 3978034 3827918 0 3881474 12354181 94845 80700963";
|
||||||
|
final static String stat_wrong_content_file = "/tmp/stat_wrong_content";
|
||||||
|
|
||||||
|
|
||||||
|
@BeforeClass
|
||||||
|
public static void init_once() {
|
||||||
|
// generate test input files once at beginning of tests
|
||||||
|
writeStr2File(stat_t0, stat_t0_file);
|
||||||
|
writeStr2File(stat_t1, stat_t1_file);
|
||||||
|
writeStr2File(stat_t2, stat_t2_file);
|
||||||
|
writeStr2File(stat_wrong_content, stat_wrong_content_file);
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterClass
|
||||||
|
public static void cleanup() {
|
||||||
|
// cleanup at end of tests
|
||||||
|
new File(stat_t0_file).delete();
|
||||||
|
new File(stat_t1_file).delete();
|
||||||
|
new File(stat_t2_file).delete();
|
||||||
|
new File(stat_wrong_content_file).delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// testcases
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testProcStatParse1() throws Exception {
|
||||||
|
ProcStatParser procStatParser = new ProcStatParser();
|
||||||
|
procStatParser.parseStatFile(stat_t0_file);
|
||||||
|
procStatParser.parseStatFile(stat_t1_file);
|
||||||
|
ProcStatCoreLoads load = procStatParser.getCpuLoad();
|
||||||
|
|
||||||
|
int l0 = (int)load.getLoad("0").floatValue();
|
||||||
|
assertEquals(8,l0);
|
||||||
|
int l1 = (int)load.getLoad("1").floatValue();
|
||||||
|
assertEquals(4,l1);
|
||||||
|
int l2 = (int)load.getLoad("2").floatValue();
|
||||||
|
assertEquals(5,l2);
|
||||||
|
int l3 = (int)load.getLoad("3").floatValue();
|
||||||
|
assertEquals(5,l3);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testProcStatParse2() throws Exception {
|
||||||
|
ProcStatParser procStatParser = new ProcStatParser();
|
||||||
|
procStatParser.parseStatFile(stat_t1_file);
|
||||||
|
procStatParser.parseStatFile(stat_t2_file);
|
||||||
|
ProcStatCoreLoads load = procStatParser.getCpuLoad();
|
||||||
|
|
||||||
|
int l0 = (int)load.getLoad("0").floatValue();
|
||||||
|
assertEquals(100,l0);
|
||||||
|
int l1 = (int)load.getLoad("1").floatValue();
|
||||||
|
assertEquals(100,l1);
|
||||||
|
int l2 = (int)load.getLoad("2").floatValue();
|
||||||
|
assertEquals(100,l2);
|
||||||
|
int l3 = (int)load.getLoad("3").floatValue();
|
||||||
|
assertEquals(100,l3);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testProcStatParse3() throws Exception {
|
||||||
|
ProcStatParser procStatParser = new ProcStatParser();
|
||||||
|
procStatParser.parseStatFile(stat_t0_file);
|
||||||
|
procStatParser.parseStatFile(stat_t2_file);
|
||||||
|
ProcStatCoreLoads load = procStatParser.getCpuLoad();
|
||||||
|
|
||||||
|
int l0 = (int)load.getLoad("0").floatValue();
|
||||||
|
assertEquals(59,l0);
|
||||||
|
int l1 = (int)load.getLoad("1").floatValue();
|
||||||
|
assertEquals(57,l1);
|
||||||
|
int l2 = (int)load.getLoad("2").floatValue();
|
||||||
|
assertEquals(57,l2);
|
||||||
|
int l3 = (int)load.getLoad("3").floatValue();
|
||||||
|
assertEquals(57,l3);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the order of the /proc/stat files to cause parsing problem
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testProcStatParseWrongOrder() throws Exception {
|
||||||
|
ProcStatParser procStatParser = new ProcStatParser();
|
||||||
|
procStatParser.parseStatFile(stat_t2_file);
|
||||||
|
procStatParser.parseStatFile(stat_t0_file);
|
||||||
|
ProcStatCoreLoads load = procStatParser.getCpuLoad();
|
||||||
|
assertEquals(load, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testProcStatParseOneSetOfCounters() throws Exception {
|
||||||
|
ProcStatParser procStatParser = new ProcStatParser();
|
||||||
|
procStatParser.parseStatFile(stat_t0_file);
|
||||||
|
ProcStatCoreLoads load = procStatParser.getCpuLoad();
|
||||||
|
|
||||||
|
int l0 = (int)load.getLoad("0").floatValue();
|
||||||
|
assertEquals(15,l0);
|
||||||
|
int l1 = (int)load.getLoad("1").floatValue();
|
||||||
|
assertEquals(14,l1);
|
||||||
|
int l2 = (int)load.getLoad("2").floatValue();
|
||||||
|
assertEquals(14,l2);
|
||||||
|
int l3 = (int)load.getLoad("3").floatValue();
|
||||||
|
assertEquals(13,l3);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected=FileNotFoundException.class)
|
||||||
|
public void testStatFileDoesNotExist() throws Exception {
|
||||||
|
ProcStatParser procStatParser = new ProcStatParser();
|
||||||
|
// read non-existing stat file
|
||||||
|
procStatParser.parseStatFile("/file/does/not/exist");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected=NumberFormatException.class)
|
||||||
|
public void testStatFileDoesntParse() throws Exception {
|
||||||
|
ProcStatParser procStatParser = new ProcStatParser();
|
||||||
|
// read non-existing stat file
|
||||||
|
procStatParser.parseStatFile(stat_wrong_content_file);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// util functions
|
||||||
|
|
||||||
|
private static void writeStr2File(String str, String fileName) {
|
||||||
|
FileWriter fileWriter = null;
|
||||||
|
File f = new File(fileName);
|
||||||
|
try {
|
||||||
|
fileWriter = new FileWriter(f);
|
||||||
|
fileWriter.write(str);
|
||||||
|
fileWriter.close();
|
||||||
|
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -7,6 +7,7 @@
|
||||||
*
|
*
|
||||||
* Contributors:
|
* Contributors:
|
||||||
* William R. Swanson (Tilera Corporation)
|
* William R. Swanson (Tilera Corporation)
|
||||||
|
* Marc Dumais (Ericsson) - Initial API and implementation (Bug 396268)
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
|
||||||
package org.eclipse.cdt.visualizer.ui;
|
package org.eclipse.cdt.visualizer.ui;
|
||||||
|
@ -37,6 +38,11 @@ public class VisualizerAction extends Action
|
||||||
// programmatically.
|
// programmatically.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Constructor. */
|
||||||
|
public VisualizerAction(String text, int style) {
|
||||||
|
super(text, style);
|
||||||
|
}
|
||||||
|
|
||||||
/** Constructor. */
|
/** Constructor. */
|
||||||
public VisualizerAction(String text, String description) {
|
public VisualizerAction(String text, String description) {
|
||||||
super(text);
|
super(text);
|
||||||
|
|
Loading…
Add table
Reference in a new issue