From 91de3531687c4da12b74c7a8c83cb422c4f061df Mon Sep 17 00:00:00 2001 From: Marc Dumais Date: Thu, 28 Feb 2013 08:17:19 -0500 Subject: [PATCH] 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 Reviewed-by: Marc Khouzam IP-Clean: Marc Khouzam Tested-by: Marc Khouzam --- .../resources/messages.properties | 13 + .../ui/actions/EnableLoadMetersAction.java | 77 +++++ .../ui/actions/SetLoadMeterPeriodAction.java | 62 ++++ .../internal/ui/model/VisualizerCPU.java | 24 ++ .../internal/ui/model/VisualizerCore.java | 24 ++ .../internal/ui/model/VisualizerLoadInfo.java | 71 ++++ .../internal/ui/model/VisualizerModel.java | 24 ++ .../view/IMulticoreVisualizerConstants.java | 36 +++ .../internal/ui/view/MulticoreVisualizer.java | 264 +++++++++++++++ .../ui/view/MulticoreVisualizerCPU.java | 61 +++- .../ui/view/MulticoreVisualizerCanvas.java | 81 ++++- .../ui/view/MulticoreVisualizerCore.java | 33 +- .../ui/view/MulticoreVisualizerLoadMeter.java | 223 +++++++++++++ .../internal/utils/DSFDebugModel.java | 63 ++++ .../internal/utils/DSFDebugModelListener.java | 14 + .../dsf/gdb/internal/ProcStatCoreLoads.java | 42 +++ .../dsf/gdb/internal/ProcStatCounters.java | 140 ++++++++ .../cdt/dsf/gdb/internal/ProcStatParser.java | 100 ++++++ .../cdt/dsf/gdb/service/GDBHardwareAndOS.java | 303 +++++++++++++++++- .../dsf/gdb/service/IGDBHardwareAndOS2.java | 27 ++ .../commands/Suite_Sessionless_Tests.java | 5 +- .../dsf/gdb/tests/ProcStatParserTest.java | 220 +++++++++++++ .../cdt/visualizer/ui/VisualizerAction.java | 6 + 23 files changed, 1891 insertions(+), 22 deletions(-) create mode 100644 dsf-gdb/org.eclipse.cdt.dsf.gdb.multicorevisualizer.ui/src/org/eclipse/cdt/dsf/gdb/multicorevisualizer/internal/ui/actions/EnableLoadMetersAction.java create mode 100644 dsf-gdb/org.eclipse.cdt.dsf.gdb.multicorevisualizer.ui/src/org/eclipse/cdt/dsf/gdb/multicorevisualizer/internal/ui/actions/SetLoadMeterPeriodAction.java create mode 100644 dsf-gdb/org.eclipse.cdt.dsf.gdb.multicorevisualizer.ui/src/org/eclipse/cdt/dsf/gdb/multicorevisualizer/internal/ui/model/VisualizerLoadInfo.java create mode 100644 dsf-gdb/org.eclipse.cdt.dsf.gdb.multicorevisualizer.ui/src/org/eclipse/cdt/dsf/gdb/multicorevisualizer/internal/ui/view/MulticoreVisualizerLoadMeter.java create mode 100644 dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/internal/ProcStatCoreLoads.java create mode 100644 dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/internal/ProcStatCounters.java create mode 100644 dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/internal/ProcStatParser.java create mode 100644 dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/ProcStatParserTest.java diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.multicorevisualizer.ui/resources/messages.properties b/dsf-gdb/org.eclipse.cdt.dsf.gdb.multicorevisualizer.ui/resources/messages.properties index 0103a53551c..62b3b75d91f 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb.multicorevisualizer.ui/resources/messages.properties +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.multicorevisualizer.ui/resources/messages.properties @@ -7,6 +7,7 @@ # # Contributors: # 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.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 diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.multicorevisualizer.ui/src/org/eclipse/cdt/dsf/gdb/multicorevisualizer/internal/ui/actions/EnableLoadMetersAction.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.multicorevisualizer.ui/src/org/eclipse/cdt/dsf/gdb/multicorevisualizer/internal/ui/actions/EnableLoadMetersAction.java new file mode 100644 index 00000000000..d4061ff46fa --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.multicorevisualizer.ui/src/org/eclipse/cdt/dsf/gdb/multicorevisualizer/internal/ui/actions/EnableLoadMetersAction.java @@ -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()); + } + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.multicorevisualizer.ui/src/org/eclipse/cdt/dsf/gdb/multicorevisualizer/internal/ui/actions/SetLoadMeterPeriodAction.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.multicorevisualizer.ui/src/org/eclipse/cdt/dsf/gdb/multicorevisualizer/internal/ui/actions/SetLoadMeterPeriodAction.java new file mode 100644 index 00000000000..5e4be80f5cb --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.multicorevisualizer.ui/src/org/eclipse/cdt/dsf/gdb/multicorevisualizer/internal/ui/actions/SetLoadMeterPeriodAction.java @@ -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); + } + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.multicorevisualizer.ui/src/org/eclipse/cdt/dsf/gdb/multicorevisualizer/internal/ui/model/VisualizerCPU.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.multicorevisualizer.ui/src/org/eclipse/cdt/dsf/gdb/multicorevisualizer/internal/ui/model/VisualizerCPU.java index 1f92161f1fd..c70117be4c8 100755 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb.multicorevisualizer.ui/src/org/eclipse/cdt/dsf/gdb/multicorevisualizer/internal/ui/model/VisualizerCPU.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.multicorevisualizer.ui/src/org/eclipse/cdt/dsf/gdb/multicorevisualizer/internal/ui/model/VisualizerCPU.java @@ -7,6 +7,7 @@ * * Contributors: * 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; @@ -29,6 +30,11 @@ public class VisualizerCPU /** ID of this core. */ public int m_id; + /** Contains load information + * @since 1.1 + */ + protected VisualizerLoadInfo m_loadinfo; + /** List of cores */ protected ArrayList m_cores; @@ -55,6 +61,7 @@ public class VisualizerCPU m_coreMap = null; m_cores.clear(); m_cores = null; + m_loadinfo = null; } } @@ -75,6 +82,23 @@ public class VisualizerCPU 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 --- diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.multicorevisualizer.ui/src/org/eclipse/cdt/dsf/gdb/multicorevisualizer/internal/ui/model/VisualizerCore.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.multicorevisualizer.ui/src/org/eclipse/cdt/dsf/gdb/multicorevisualizer/internal/ui/model/VisualizerCore.java index 87bc1c55e96..4d2e6571d44 100755 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb.multicorevisualizer.ui/src/org/eclipse/cdt/dsf/gdb/multicorevisualizer/internal/ui/model/VisualizerCore.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.multicorevisualizer.ui/src/org/eclipse/cdt/dsf/gdb/multicorevisualizer/internal/ui/model/VisualizerCore.java @@ -7,6 +7,7 @@ * * Contributors: * 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; @@ -23,6 +24,10 @@ public class VisualizerCore /** Linux CPU ID of this core. */ public int m_id = 0; + /** Contains load information + * @since 1.1 + */ + protected VisualizerLoadInfo m_loadinfo; // --- constructors/destructors --- @@ -34,6 +39,7 @@ public class VisualizerCore /** Dispose method */ public void dispose() { + m_loadinfo = null; } @@ -57,6 +63,24 @@ public class VisualizerCore public int getID() { 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 --- diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.multicorevisualizer.ui/src/org/eclipse/cdt/dsf/gdb/multicorevisualizer/internal/ui/model/VisualizerLoadInfo.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.multicorevisualizer.ui/src/org/eclipse/cdt/dsf/gdb/multicorevisualizer/internal/ui/model/VisualizerLoadInfo.java new file mode 100644 index 00000000000..f6cef401446 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.multicorevisualizer.ui/src/org/eclipse/cdt/dsf/gdb/multicorevisualizer/internal/ui/model/VisualizerLoadInfo.java @@ -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; + } + + +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.multicorevisualizer.ui/src/org/eclipse/cdt/dsf/gdb/multicorevisualizer/internal/ui/model/VisualizerModel.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.multicorevisualizer.ui/src/org/eclipse/cdt/dsf/gdb/multicorevisualizer/internal/ui/model/VisualizerModel.java index d62a71435d7..bdddd3bdf25 100755 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb.multicorevisualizer.ui/src/org/eclipse/cdt/dsf/gdb/multicorevisualizer/internal/ui/model/VisualizerModel.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.multicorevisualizer.ui/src/org/eclipse/cdt/dsf/gdb/multicorevisualizer/internal/ui/model/VisualizerModel.java @@ -7,6 +7,7 @@ * * Contributors: * 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; @@ -38,12 +39,17 @@ public class VisualizerModel /** Completion state tracker. */ protected Todo m_todo; + /** Completion state tracker for load meters. */ + protected Todo m_loadTodo; + // Setting to remove exited threads, or keep them shown. // If we are to support this, we should have a preference // and a way to for the user to clean up old threads, // or maybe a timeout to remove them. private boolean m_keepExitedThreads = false; + protected boolean m_loadMetersEnabled = false; + // --- constructors/destructors --- /** Constructor */ @@ -52,6 +58,7 @@ public class VisualizerModel m_cpuMap = new Hashtable(); m_threads = new ArrayList(); m_todo = new Todo(); + m_loadTodo = new Todo(); } /** Dispose method */ @@ -76,6 +83,10 @@ public class VisualizerModel m_todo.dispose(); m_todo = null; } + if (m_loadTodo != null) { + m_loadTodo.dispose(); + m_loadTodo = null; + } } @@ -86,6 +97,19 @@ public class VisualizerModel 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 --- /** Sorts cores, cpus, etc. by IDs. */ diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.multicorevisualizer.ui/src/org/eclipse/cdt/dsf/gdb/multicorevisualizer/internal/ui/view/IMulticoreVisualizerConstants.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.multicorevisualizer.ui/src/org/eclipse/cdt/dsf/gdb/multicorevisualizer/internal/ui/view/IMulticoreVisualizerConstants.java index 5b3a280cacc..6ec0634e24f 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb.multicorevisualizer.ui/src/org/eclipse/cdt/dsf/gdb/multicorevisualizer/internal/ui/view/IMulticoreVisualizerConstants.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.multicorevisualizer.ui/src/org/eclipse/cdt/dsf/gdb/multicorevisualizer/internal/ui/view/IMulticoreVisualizerConstants.java @@ -7,6 +7,7 @@ * * Contributors: * 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; @@ -49,6 +50,17 @@ public class IMulticoreVisualizerConstants public static final Color COLOR_CRASHED_CORE_FG = Colors.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 /** 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 */ public static final Color COLOR_CORE_TEXT_FG = Colors.WHITE; 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); } diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.multicorevisualizer.ui/src/org/eclipse/cdt/dsf/gdb/multicorevisualizer/internal/ui/view/MulticoreVisualizer.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.multicorevisualizer.ui/src/org/eclipse/cdt/dsf/gdb/multicorevisualizer/internal/ui/view/MulticoreVisualizer.java index f9ea96ac601..76910a4fa53 100755 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb.multicorevisualizer.ui/src/org/eclipse/cdt/dsf/gdb/multicorevisualizer/internal/ui/view/MulticoreVisualizer.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.multicorevisualizer.ui/src/org/eclipse/cdt/dsf/gdb/multicorevisualizer/internal/ui/view/MulticoreVisualizer.java @@ -9,10 +9,12 @@ * William R. Swanson (Tilera Corporation) - initial API and implementation * IBM Corporation * Marc Dumais (Ericsson) - Bug 399281 + * Marc Dumais (Ericsson) - Add CPU/core load information to the multicore visualizer (Bug 396268) *******************************************************************************/ package org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.ui.view; +import java.util.ArrayList; import java.util.HashSet; import java.util.List; 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.gdb.launching.GDBProcess; import org.eclipse.cdt.dsf.gdb.launching.GdbLaunch; +import org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.ui.MulticoreVisualizerUIPlugin; +import org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.ui.actions.EnableLoadMetersAction; import org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.ui.actions.RefreshAction; import org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.ui.actions.SelectAllAction; +import org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.ui.actions.SetLoadMeterPeriodAction; import org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.ui.model.VisualizerCPU; import org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.ui.model.VisualizerCore; import org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.ui.model.VisualizerExecutionState; +import org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.ui.model.VisualizerLoadInfo; import org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.ui.model.VisualizerModel; import org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.ui.model.VisualizerThread; import org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.utils.DSFDebugModel; @@ -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.service.IGDBHardwareAndOS.ICPUDMContext; import org.eclipse.cdt.dsf.gdb.service.IGDBHardwareAndOS.ICoreDMContext; +import org.eclipse.cdt.dsf.gdb.service.IGDBHardwareAndOS2.ILoadInfo; import org.eclipse.cdt.dsf.mi.service.IMIExecutionDMContext; import org.eclipse.cdt.dsf.mi.service.IMIProcessDMContext; import org.eclipse.cdt.dsf.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.GUIUtils; import org.eclipse.cdt.visualizer.ui.util.SelectionUtils; +import org.eclipse.cdt.visualizer.ui.util.Timer; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.debug.core.ILaunch; import org.eclipse.debug.internal.ui.commands.actions.DropToFrameCommandAction; @@ -63,6 +71,7 @@ import org.eclipse.debug.internal.ui.views.launch.LaunchView; import org.eclipse.debug.ui.DebugUITools; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.action.IToolBarManager; +import org.eclipse.jface.action.MenuManager; import org.eclipse.jface.action.Separator; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.SelectionChangedEvent; @@ -107,6 +116,39 @@ public class MulticoreVisualizer extends GraphicCanvasVisualizer /** Model changed listener, attached to Debug View. */ 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 --- @@ -143,6 +185,17 @@ public class MulticoreVisualizer extends GraphicCanvasVisualizer /** Toolbar / menu action */ 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 m_setLoadMeterPeriodActions = null; + // --- constructors/destructors --- @@ -158,6 +211,7 @@ public class MulticoreVisualizer extends GraphicCanvasVisualizer super.dispose(); removeDebugViewerListener(); disposeActions(); + disposeLoadMeterTimer(); } @@ -169,6 +223,26 @@ public class MulticoreVisualizer extends GraphicCanvasVisualizer 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. */ @Override public void disposeVisualizer() @@ -198,6 +272,28 @@ public class MulticoreVisualizer extends GraphicCanvasVisualizer 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 --- @@ -219,6 +315,7 @@ public class MulticoreVisualizer extends GraphicCanvasVisualizer m_canvas.dispose(); m_canvas = null; } + disposeLoadMeterTimer(); } /** Invoked after visualizer control creation, */ @@ -285,6 +382,37 @@ public class MulticoreVisualizer extends GraphicCanvasVisualizer m_refreshAction = new RefreshAction(); m_refreshAction.init(this); + + // create load meters sub-menu and associated actions + m_loadMetersSubMenu = new MenuManager(MulticoreVisualizerUIPlugin.getString( + "MulticoreVisualizer.actions.LoadMeterSubmenu.text")); //$NON-NLS-1$ + m_loadMetersRefreshSubSubmenu = new MenuManager(MulticoreVisualizerUIPlugin.getString( + "MulticoreVisualizer.actions.LoadMetersRefreshSubSubmenu.text")); //$NON-NLS-1$ + + m_enableLoadMetersAction = new EnableLoadMetersAction(m_loadMetersEnabled); + m_enableLoadMetersAction.init(this); + // enable the load meter sub-menu + m_enableLoadMetersAction.setEnabled(true); + + m_setLoadMeterPeriodActions = new ArrayList(); + 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, // so we'll pretend the actions are not yet updated, @@ -301,6 +429,10 @@ public class MulticoreVisualizer extends GraphicCanvasVisualizer m_selectAllAction.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 // actions, as they are automatically enabled/disabled // by the platform. @@ -358,6 +490,30 @@ public class MulticoreVisualizer extends GraphicCanvasVisualizer 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; } @@ -420,6 +576,21 @@ public class MulticoreVisualizer extends GraphicCanvasVisualizer menuManager.add(m_selectAllAction); menuManager.add(m_refreshAction); + menuManager.add(m_separatorAction); + + // add load meters sub-menus and actions + m_loadMetersSubMenu.removeAll(); + m_loadMetersRefreshSubSubmenu.removeAll(); + + menuManager.add(m_loadMetersSubMenu); + + m_loadMetersSubMenu.add(m_enableLoadMetersAction); + m_loadMetersSubMenu.add(m_loadMetersRefreshSubSubmenu); + + for (SetLoadMeterPeriodAction act : m_setLoadMeterPeriodActions) { + m_loadMetersRefreshSubSubmenu.add(act); + } + updateActions(); Point location = m_viewer.getContextMenuLocation(); updateContextMenuActions(location); @@ -663,6 +834,11 @@ public class MulticoreVisualizer extends GraphicCanvasVisualizer if (m_sessionState != null && ! 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.dispose(); m_sessionState = null; @@ -674,6 +850,8 @@ public class MulticoreVisualizer extends GraphicCanvasVisualizer { m_sessionState = new DSFSessionState(sessionId); m_sessionState.addServiceEventListener(fEventListener); + // start timer that updates the load meters + initializeLoadMeterTimer(); changed = true; } @@ -762,6 +940,8 @@ public class MulticoreVisualizer extends GraphicCanvasVisualizer /** Invoked when getModel() request completes. */ @ConfinedToDsfExecutor("getSession().getExecutor()") public void getVisualizerModelDone(VisualizerModel model) { + fDataModel.setLoadMetersEnabled(m_loadMetersEnabled); + updateLoads(); model.sort(); setCanvasModel(model); } @@ -774,6 +954,8 @@ public class MulticoreVisualizer extends GraphicCanvasVisualizer @ConfinedToDsfExecutor("getSession().getExecutor()") public void getCPUsDone(ICPUDMContext[] cpuContexts, Object arg) { + // save CPU contexts + m_cpuContextsCache = cpuContexts; VisualizerModel model = (VisualizerModel) arg; if (cpuContexts == null || cpuContexts.length == 0) { @@ -811,6 +993,8 @@ public class MulticoreVisualizer extends GraphicCanvasVisualizer ICoreDMContext[] coreContexts, Object arg) { + // save core contexts + m_coreContextsCache = coreContexts; VisualizerModel model = (VisualizerModel) arg; if (coreContexts == null || coreContexts.length == 0) { @@ -924,6 +1108,70 @@ public class MulticoreVisualizer extends GraphicCanvasVisualizer 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. */ protected void done(int n, VisualizerModel model) { model.getTodo().done(n); @@ -931,5 +1179,21 @@ public class MulticoreVisualizer extends GraphicCanvasVisualizer 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(); + } + } + } } diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.multicorevisualizer.ui/src/org/eclipse/cdt/dsf/gdb/multicorevisualizer/internal/ui/view/MulticoreVisualizerCPU.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.multicorevisualizer.ui/src/org/eclipse/cdt/dsf/gdb/multicorevisualizer/internal/ui/view/MulticoreVisualizerCPU.java index fc0cf24f29d..423610b31a2 100755 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb.multicorevisualizer.ui/src/org/eclipse/cdt/dsf/gdb/multicorevisualizer/internal/ui/view/MulticoreVisualizerCPU.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.multicorevisualizer.ui/src/org/eclipse/cdt/dsf/gdb/multicorevisualizer/internal/ui/view/MulticoreVisualizerCPU.java @@ -7,6 +7,7 @@ * * Contributors: * 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; @@ -14,7 +15,6 @@ package org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.ui.view; import java.util.ArrayList; import java.util.List; -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; @@ -32,6 +32,22 @@ public class MulticoreVisualizerCPU extends MulticoreVisualizerGraphicObject /** Child cores. */ protected ArrayList 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 --- @@ -40,12 +56,18 @@ public class MulticoreVisualizerCPU extends MulticoreVisualizerGraphicObject { m_id = id; m_cores = new ArrayList(); + + // default load meter + m_loadMeter = new MulticoreVisualizerLoadMeter(null, null); } /** Dispose method */ @Override public void dispose() { super.dispose(); + if (m_loadMeter != null) { + m_loadMeter.dispose(); + } } @@ -77,17 +99,30 @@ public class MulticoreVisualizerCPU extends MulticoreVisualizerGraphicObject 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 --- /** Invoked to allow element to paint itself on the viewer canvas */ @Override public void paintContent(GC gc) { - Color fg, bg; - fg = Colors.getColor(0,255,0); - bg = Colors.getColor(0,64,0); - gc.setForeground(fg); - gc.setBackground(bg); + gc.setForeground(FG_COLOR); + gc.setBackground(BG_COLOR); + + // We want the load meter to share the same BG color + m_loadMeter.setParentBgColor(BG_COLOR); gc.fillRectangle(m_bounds); gc.drawRectangle(m_bounds); @@ -103,15 +138,13 @@ public class MulticoreVisualizerCPU extends MulticoreVisualizerGraphicObject @Override public void paintDecorations(GC gc) { if (m_bounds.height > 20) { - Color fg, bg; - fg = Colors.getColor(0,255,0); - bg = Colors.getColor(0,64,0); - gc.setForeground(fg); - gc.setBackground(bg); + gc.setForeground(IMulticoreVisualizerConstants.COLOR_CPU_FG); + gc.setBackground(IMulticoreVisualizerConstants.COLOR_CPU_BG); - int text_indent = 6; - int tx = m_bounds.x + m_bounds.width - text_indent; - int ty = m_bounds.y + m_bounds.height - text_indent; + int text_indent_x = 6; + int text_indent_y = 2; + 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); } } diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.multicorevisualizer.ui/src/org/eclipse/cdt/dsf/gdb/multicorevisualizer/internal/ui/view/MulticoreVisualizerCanvas.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.multicorevisualizer.ui/src/org/eclipse/cdt/dsf/gdb/multicorevisualizer/internal/ui/view/MulticoreVisualizerCanvas.java index 093501c1cb4..9ba43feeb85 100755 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb.multicorevisualizer.ui/src/org/eclipse/cdt/dsf/gdb/multicorevisualizer/internal/ui/view/MulticoreVisualizerCanvas.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.multicorevisualizer.ui/src/org/eclipse/cdt/dsf/gdb/multicorevisualizer/internal/ui/view/MulticoreVisualizerCanvas.java @@ -12,11 +12,13 @@ * Marc Dumais (Ericsson) - Bug 396200 * Marc Dumais (Ericsson) - Bug 396293 * 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; import java.util.ArrayList; +import java.util.Enumeration; import java.util.HashSet; import java.util.Hashtable; import java.util.List; @@ -76,6 +78,12 @@ public class MulticoreVisualizerCanvas extends GraphicCanvas */ 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 */ protected boolean m_update = true; @@ -283,6 +291,13 @@ public class MulticoreVisualizerCanvas extends GraphicCanvas requestUpdate(); } + /** + * only update the load meters + * @since 1.1 + */ + public void refreshLoadMeters() { + requestRecache(false, false, true); + } // --- resize methods --- @@ -316,16 +331,26 @@ public class MulticoreVisualizerCanvas extends GraphicCanvas /** Requests that next paint call should recache state and/or size information */ // synchronized so we don't change recache flags while doing a recache public synchronized void requestRecache() { - requestRecache(true, true); + requestRecache(true, true, true); } /** Requests that next paint call should recache state and/or size information */ // synchronized so we don't change recache flags while doing a recache 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; // NOTE: we intentionally OR these flags with any pending request(s) m_recacheState |= state; m_recacheSizes |= sizes; + m_recacheLoadMeters |= load; } /** Fits n square items into a rectangle of the specified size. @@ -372,6 +397,7 @@ public class MulticoreVisualizerCanvas extends GraphicCanvas m_threads.clear(); m_cpuMap.clear(); m_coreMap.clear(); + m_threadMap.clear(); // For debugging purposes only, allows us to force a CPU count. //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_recacheLoadMeters = true; m_recacheSizes = true; } + if (m_recacheLoadMeters) { + // refresh the visualizer CPU and core load meters + if (m_model != null) { + Enumeration 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) { // avoid doing resize calculations if the model is not ready 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_separation = 6; // spacing between CPUS - int core_margin = 4; // margin around cores in a CPU - int core_separation = 2; // spacing between cores + // make room when load meters are present, else use a more compact layout + 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. Rectangle bounds = getClientArea(); GUIUtils.inset(bounds, cpu_margin); @@ -442,6 +502,8 @@ public class MulticoreVisualizerCanvas extends GraphicCanvas int x = bounds.x, y = bounds.y; for (MulticoreVisualizerCPU cpu : m_cpus) { 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 cx = left, cy = y + core_margin; @@ -449,6 +511,13 @@ public class MulticoreVisualizerCanvas extends GraphicCanvas { 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; if (cx + core_size + core_margin > x + cpu_size) { cx = left; @@ -564,11 +633,15 @@ public class MulticoreVisualizerCanvas extends GraphicCanvas // paint cpus for (MulticoreVisualizerCPU cpu : m_cpus) { cpu.paintContent(gc); + cpu.getLoadMeter().paintContent(gc); + cpu.getLoadMeter().paintDecorations(gc); } // paint cores for (MulticoreVisualizerCore core : m_cores) { core.paintContent(gc); + core.getLoadMeter().paintContent(gc); + core.getLoadMeter().paintDecorations(gc); } // paint cpus IDs on top of cores diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.multicorevisualizer.ui/src/org/eclipse/cdt/dsf/gdb/multicorevisualizer/internal/ui/view/MulticoreVisualizerCore.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.multicorevisualizer.ui/src/org/eclipse/cdt/dsf/gdb/multicorevisualizer/internal/ui/view/MulticoreVisualizerCore.java index 2772a058ea7..37359cc768b 100755 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb.multicorevisualizer.ui/src/org/eclipse/cdt/dsf/gdb/multicorevisualizer/internal/ui/view/MulticoreVisualizerCore.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.multicorevisualizer.ui/src/org/eclipse/cdt/dsf/gdb/multicorevisualizer/internal/ui/view/MulticoreVisualizerCore.java @@ -7,6 +7,7 @@ * * Contributors: * 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; @@ -36,6 +37,12 @@ public class MulticoreVisualizerCore extends MulticoreVisualizerGraphicObject /** List of threads currently on this core. */ protected ArrayList m_threads; + /** + * Load meter associated to this core + * @since 1.1 + */ + protected MulticoreVisualizerLoadMeter m_loadMeter; + // --- constructors/destructors --- /** Constructor */ @@ -44,6 +51,9 @@ public class MulticoreVisualizerCore extends MulticoreVisualizerGraphicObject if (m_cpu != null) m_cpu.addCore(this); m_id = id; m_threads = new ArrayList(); + + // default load meter + m_loadMeter = new MulticoreVisualizerLoadMeter(null, null); } /** Dispose method */ @@ -54,6 +64,9 @@ public class MulticoreVisualizerCore extends MulticoreVisualizerGraphicObject m_threads.clear(); m_threads = null; } + if (m_loadMeter != null) { + m_loadMeter.dispose(); + } } @@ -95,6 +108,20 @@ public class MulticoreVisualizerCore extends MulticoreVisualizerGraphicObject { 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. @@ -149,8 +176,12 @@ public class MulticoreVisualizerCore extends MulticoreVisualizerGraphicObject /** Invoked to allow element to paint itself on the viewer canvas */ @Override public void paintContent(GC gc) { + Color bg = getCoreStateColor(false); + 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.drawRectangle(m_bounds); diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.multicorevisualizer.ui/src/org/eclipse/cdt/dsf/gdb/multicorevisualizer/internal/ui/view/MulticoreVisualizerLoadMeter.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.multicorevisualizer.ui/src/org/eclipse/cdt/dsf/gdb/multicorevisualizer/internal/ui/view/MulticoreVisualizerLoadMeter.java new file mode 100644 index 00000000000..9a55a98d196 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.multicorevisualizer.ui/src/org/eclipse/cdt/dsf/gdb/multicorevisualizer/internal/ui/view/MulticoreVisualizerLoadMeter.java @@ -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); + } + } + +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.multicorevisualizer.ui/src/org/eclipse/cdt/dsf/gdb/multicorevisualizer/internal/utils/DSFDebugModel.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.multicorevisualizer.ui/src/org/eclipse/cdt/dsf/gdb/multicorevisualizer/internal/utils/DSFDebugModel.java index d7b65a1020e..add78f58485 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb.multicorevisualizer.ui/src/org/eclipse/cdt/dsf/gdb/multicorevisualizer/internal/utils/DSFDebugModel.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.multicorevisualizer.ui/src/org/eclipse/cdt/dsf/gdb/multicorevisualizer/internal/utils/DSFDebugModel.java @@ -7,6 +7,7 @@ * * Contributors: * 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; @@ -14,6 +15,7 @@ package org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.utils; import java.util.ArrayList; 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.ImmediateDataRequestMonitor; 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.ICoreDMContext; 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.mi.service.IMIExecutionDMContext; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.visualizer.ui.util.Timer; /** 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() { + @Override + protected void handleCompleted() { + listener.getLoadDone(context, getData(), arg); + } + } + ); + } + /** Requests list of Cores. * Calls back to getCoresDone() on listener. */ @ConfinedToDsfExecutor("getSession().getExecutor()") @@ -337,4 +368,36 @@ public class DSFDebugModel { 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; + } } diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.multicorevisualizer.ui/src/org/eclipse/cdt/dsf/gdb/multicorevisualizer/internal/utils/DSFDebugModelListener.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.multicorevisualizer.ui/src/org/eclipse/cdt/dsf/gdb/multicorevisualizer/internal/utils/DSFDebugModelListener.java index f55a2594d9a..2336d6de0f0 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb.multicorevisualizer.ui/src/org/eclipse/cdt/dsf/gdb/multicorevisualizer/internal/utils/DSFDebugModelListener.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.multicorevisualizer.ui/src/org/eclipse/cdt/dsf/gdb/multicorevisualizer/internal/utils/DSFDebugModelListener.java @@ -7,6 +7,7 @@ * * Contributors: * 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; @@ -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.service.IGDBHardwareAndOS.ICPUDMContext; import org.eclipse.cdt.dsf.gdb.service.IGDBHardwareAndOS.ICoreDMContext; +import org.eclipse.cdt.dsf.gdb.service.IGDBHardwareAndOS2.ILoadInfo; import org.eclipse.cdt.dsf.mi.service.IMIExecutionDMContext; /** Interface for classes that interact with DSFDebugModel. @@ -64,5 +66,17 @@ public interface DSFDebugModelListener { IThreadDMData threadData, VisualizerExecutionState state, 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(); + } diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/internal/ProcStatCoreLoads.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/internal/ProcStatCoreLoads.java new file mode 100644 index 00000000000..fb19f8c486e --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/internal/ProcStatCoreLoads.java @@ -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 m_coreLoads; + + public ProcStatCoreLoads() { + m_coreLoads = new HashMap(); + } + + 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$ + } +} + diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/internal/ProcStatCounters.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/internal/ProcStatCounters.java new file mode 100644 index 00000000000..c0e1259dcc0 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/internal/ProcStatCounters.java @@ -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 fTickCounters = new HashMap(); + + /** + * 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(); + } + + /** + * 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; + } + +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/internal/ProcStatParser.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/internal/ProcStatParser.java new file mode 100644 index 00000000000..a69d5cb17ff --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/internal/ProcStatParser.java @@ -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 ticks = new Vector(); + 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); + } + +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBHardwareAndOS.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBHardwareAndOS.java index 5a803476829..6a69334d4bf 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBHardwareAndOS.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBHardwareAndOS.java @@ -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 * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -8,17 +8,22 @@ * Contributors: * Marc Khouzam (Ericsson) - initial API and implementation * 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; import java.io.File; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.HashSet; import java.util.Hashtable; import java.util.List; +import java.util.Map; +import java.util.Map.Entry; import java.util.Set; import java.util.Vector; +import java.util.concurrent.TimeUnit; import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; 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.RequestMonitor; 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.IDMContext; 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.CoreList; 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.output.MIMetaGetCPUInfoInfo; import org.eclipse.cdt.dsf.gdb.service.command.IGDBControl; @@ -65,7 +73,7 @@ import org.osgi.framework.BundleContext; * * @since 4.1 */ -public class GDBHardwareAndOS extends AbstractDsfService implements IGDBHardwareAndOS, ICachingService { +public class GDBHardwareAndOS extends AbstractDsfService implements IGDBHardwareAndOS2, ICachingService { @Immutable 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 fDetailedLoad; + + public GDBLoadInfo(String load, Map detailedLoad) { + fLoad = load; + fDetailedLoad = detailedLoad; + } + public GDBLoadInfo(String load) { + this(load, null); + } + @Override + public String getLoad() { + return fLoad; + } + @Override + public Map getDetailedLoad() { + return fDetailedLoad; + } + } + + // to save queued load info requests for later processing + private Map> fLoadInfoRequestCache; + private IGDBControl fCommandControl; private IGDBBackend fBackend; private CommandFactory fCommandFactory; @@ -170,6 +206,19 @@ public class GDBHardwareAndOS extends AbstractDsfService implements IGDBHardware // Bug 374293 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) { super(session); } @@ -211,11 +260,13 @@ public class GDBHardwareAndOS extends AbstractDsfService implements IGDBHardware // handle getting the required cpu info fFetchCPUInfoCache = new CommandCache(getSession(), new CPUInfoManager()); fFetchCPUInfoCache.setContextAvailable(fCommandControl.getContext(), true); + fLoadInfoRequestCache = new HashMap>(); getSession().addServiceEventListener(this, null); // Register this service. register(new String[] { IGDBHardwareAndOS.class.getName(), + IGDBHardwareAndOS2.class.getName(), GDBHardwareAndOS.class.getName() }, new Hashtable()); @@ -234,6 +285,7 @@ public class GDBHardwareAndOS extends AbstractDsfService implements IGDBHardware public void shutdown(RequestMonitor requestMonitor) { getSession().removeServiceEventListener(this); fFetchCPUInfoCache.reset(); + fLoadInfoRequestCache.clear(); unregister(); 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$ } + + /** + * @since 4.2 + */ + @Override + public boolean isAvailable() { + return false; + } + + /** + * @since 4.2 + */ + @Override + public void getResourceClasses(IDMContext dmc, + DataRequestMonitor 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 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 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(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(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> 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> 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 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() { + @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))); + } + } + ); + } + } } diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/IGDBHardwareAndOS2.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/IGDBHardwareAndOS2.java index 256c67dd93f..7c0c3bd047b 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/IGDBHardwareAndOS2.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/IGDBHardwareAndOS2.java @@ -7,10 +7,13 @@ * * Contributors: * 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; +import java.util.Map; + import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; import org.eclipse.cdt.dsf.datamodel.IDMContext; @@ -64,4 +67,28 @@ public interface IGDBHardwareAndOS2 extends IGDBHardwareAndOS { * Return table describing resources of specified class. */ void getResourcesInformation(IDMContext dmc, String resourceClassId, DataRequestMonitor 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 getDetailedLoad(); + } + + /** + * Computes CPU/core load information according to context and + * asynchronously returns the result in a ILoadInfo object + */ + void getLoadInfo(IDMContext dmc, DataRequestMonitor rm); } diff --git a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/Suite_Sessionless_Tests.java b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/Suite_Sessionless_Tests.java index de08ef198d9..3039dee5b4e 100644 --- a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/Suite_Sessionless_Tests.java +++ b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/Suite_Sessionless_Tests.java @@ -7,6 +7,7 @@ * * Contributors: * Ericsson - Initial Implementation + * Marc Dumais (Ericsson) - Initial API and implementation (Bug 396268) *******************************************************************************/ 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.tests.dsf.gdb.framework.OnceOnlySuite; 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.runners.Suite; @@ -29,7 +31,8 @@ import org.junit.runners.Suite; TestMICommandConstructCommand.class, MIThreadTests.class, LaunchUtilsTest.class, - MIStringHandlerTests.class + MIStringHandlerTests.class, + ProcStatParserTest.class /* Add your test class here */ }) public class Suite_Sessionless_Tests { diff --git a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/ProcStatParserTest.java b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/ProcStatParserTest.java new file mode 100644 index 00000000000..23f2ede4126 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/tests/ProcStatParserTest.java @@ -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(); + } + } + +} diff --git a/visualizer/org.eclipse.cdt.visualizer.ui/src/org/eclipse/cdt/visualizer/ui/VisualizerAction.java b/visualizer/org.eclipse.cdt.visualizer.ui/src/org/eclipse/cdt/visualizer/ui/VisualizerAction.java index 0098d84854d..d9418221edc 100755 --- a/visualizer/org.eclipse.cdt.visualizer.ui/src/org/eclipse/cdt/visualizer/ui/VisualizerAction.java +++ b/visualizer/org.eclipse.cdt.visualizer.ui/src/org/eclipse/cdt/visualizer/ui/VisualizerAction.java @@ -7,6 +7,7 @@ * * Contributors: * William R. Swanson (Tilera Corporation) + * Marc Dumais (Ericsson) - Initial API and implementation (Bug 396268) *******************************************************************************/ package org.eclipse.cdt.visualizer.ui; @@ -37,6 +38,11 @@ public class VisualizerAction extends Action // programmatically. } + /** Constructor. */ + public VisualizerAction(String text, int style) { + super(text, style); + } + /** Constructor. */ public VisualizerAction(String text, String description) { super(text);