1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-04-29 19:45:01 +02:00

Bug 460837 - [visualizer] Add persistent information storage in the MV

Change-Id: I18bf08043c17f3bf4c1a2da86cbd6ef5b46d0120
This commit is contained in:
Marc Dumais 2015-02-27 09:41:26 -05:00
parent 498bfb6509
commit d38aa32296
4 changed files with 451 additions and 14 deletions

View file

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2012 Ericsson and others.
* Copyright (c) 2012, 2015 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,6 +8,7 @@
* Contributors:
* Marc Khouzam (Ericsson) - initial API and implementation
* William R. Swanson (Tilera Corporation) - added resource support
* Marc Dumais (Ericsson) - Bug 460837
*******************************************************************************/
package org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.ui;
@ -16,6 +17,8 @@ import org.eclipse.cdt.visualizer.ui.plugin.CDTVisualizerUIPlugin;
import org.eclipse.cdt.visualizer.ui.util.UIResourceManager;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.preferences.IEclipsePreferences;
import org.eclipse.core.runtime.preferences.InstanceScope;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.swt.graphics.Font;
@ -283,4 +286,9 @@ public class MulticoreVisualizerUIPlugin extends AbstractUIPlugin
public static Font getFont(String fontName, int height, int style) {
return getDefault().getPluginResources().getFont(fontName, height, style);
}
/** Get the preference store for this Eclipse plug-in */
public static IEclipsePreferences getEclipsePreferenceStore() {
return InstanceScope.INSTANCE.getNode(PLUGIN_ID);
}
}

View file

@ -24,6 +24,7 @@
* Marc Dumais (Ericsson) - Bug 458076
* Alvaro Sanchez-Leon (Ericsson) - Bug 459114 - override construction of the data model
* Marc Dumais (Ericsson) - Bug 460737
* Marc Dumais (Ericsson) - Bug 460837
*******************************************************************************/
package org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.ui.view;
@ -62,6 +63,8 @@ import org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.utils.DSFDebugModel;
import org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.utils.DSFSessionState;
import org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.utils.DebugViewUtils;
import org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.utils.IDSFTargetDataProxy;
import org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.utils.PersistentSettingsManager;
import org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.utils.PersistentSettingsManager.PersistentParameter;
import org.eclipse.cdt.dsf.gdb.service.IGDBHardwareAndOS.ICPUDMContext;
import org.eclipse.cdt.dsf.gdb.service.IGDBHardwareAndOS.ICoreDMContext;
import org.eclipse.cdt.dsf.gdb.service.IGDBHardwareAndOS2.ILoadInfo;
@ -158,11 +161,13 @@ public class MulticoreVisualizer extends GraphicCanvasVisualizer implements IPin
/** Main switch that determines if we should display the load meters */
protected boolean m_loadMetersEnabled = false;
private PersistentParameter<Boolean> m_loadMetersEnabled;
/** Timer used to trigger the update of the CPU/core load meters */
protected Timer m_updateLoadMeterTimer = null;
/** update period for the load meters */
protected int m_loadMeterTimerPeriod = LOAD_METER_TIMER_MEDIUM; // default 1000ms
private PersistentParameter<Integer> m_loadMeterTimerPeriod;
// Load meters refresh periods, in ms
/** constant for the very short load meters update period */
@ -233,6 +238,9 @@ public class MulticoreVisualizer extends GraphicCanvasVisualizer implements IPin
/** Menu action */
protected PinToDebugSessionAction m_pinToDbgSessionAction = null;
/** persistent settings manager */
protected PersistentSettingsManager m_persistentSettingsManager = null;
// --- constructors/destructors ---
@ -272,14 +280,29 @@ public class MulticoreVisualizer extends GraphicCanvasVisualizer implements IPin
if (m_visualizerInstanceId == null) {
m_visualizerInstanceId = "0"; //$NON-NLS-1$
}
initializePersistentParameters(m_visualizerInstanceId);
}
/**
* Initialize the persistent parameters
*/
protected void initializePersistentParameters(String visualizerInstanceId) {
// setting managers
m_persistentSettingsManager = new PersistentSettingsManager("MulticoreVisualizer", visualizerInstanceId); //$NON-NLS-1$
// define persistent parameters:
m_loadMetersEnabled = m_persistentSettingsManager.getNewParameter(Boolean.class,
"enableLoadMeters", true, false); //$NON-NLS-1$
m_loadMeterTimerPeriod = m_persistentSettingsManager.getNewParameter(Integer.class,
"loadMeterTimerPeriod", true, LOAD_METER_TIMER_MEDIUM); //$NON-NLS-1$
}
/**
* Sets-up the timer associated to load meters refresh
*/
protected void initializeLoadMeterTimer() {
if (!m_loadMetersEnabled) return;
m_updateLoadMeterTimer = getLoadTimer(m_sessionState, m_loadMeterTimerPeriod);
if (!getLoadMetersEnabled()) return;
m_updateLoadMeterTimer = getLoadTimer(m_sessionState, getLoadMeterTimerPeriod());
// one-shot timer (re-scheduled upon successful triggering)
m_updateLoadMeterTimer.setRepeating(false);
}
@ -328,24 +351,34 @@ public class MulticoreVisualizer extends GraphicCanvasVisualizer implements IPin
*/
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;
if (getLoadMeterTimerPeriod() == p) return;
m_loadMeterTimerPeriod.set(p > LOAD_METER_TIMER_MIN ? p : LOAD_METER_TIMER_MIN);
disposeLoadMeterTimer();
initializeLoadMeterTimer();
}
/** Gets the load meter period */
public int getLoadMeterTimerPeriod() {
return m_loadMeterTimerPeriod != null ? m_loadMeterTimerPeriod.value() : 0;
}
/**
* enables or disables the load meters
*/
public void setLoadMetersEnabled(boolean enabled) {
if (m_loadMetersEnabled == enabled) return;
m_loadMetersEnabled = enabled;
if (m_loadMetersEnabled.value() == enabled) return;
m_loadMetersEnabled.set(enabled);
// save load meter enablement in model
fDataModel.setLoadMetersEnabled(m_loadMetersEnabled);
fDataModel.setLoadMetersEnabled(getLoadMetersEnabled());
disposeLoadMeterTimer();
initializeLoadMeterTimer();
}
/** Returns whether the load meters are enabled */
public boolean getLoadMetersEnabled() {
return m_loadMetersEnabled != null? m_loadMetersEnabled.value() : false;
}
// --- canvas management ---
/** Creates and returns visualizer canvas control. */
@ -457,7 +490,7 @@ public class MulticoreVisualizer extends GraphicCanvasVisualizer implements IPin
m_loadMetersRefreshSubSubmenu = new MenuManager(MulticoreVisualizerUIPlugin.getString(
"MulticoreVisualizer.actions.LoadMetersRefreshSubSubmenu.text")); //$NON-NLS-1$
m_enableLoadMetersAction = new EnableLoadMetersAction(m_loadMetersEnabled);
m_enableLoadMetersAction = new EnableLoadMetersAction(getLoadMetersEnabled());
m_enableLoadMetersAction.init(this);
// enable the load meter sub-menu
m_enableLoadMetersAction.setEnabled(true);
@ -467,6 +500,7 @@ public class MulticoreVisualizer extends GraphicCanvasVisualizer implements IPin
MulticoreVisualizerUIPlugin.getString("MulticoreVisualizer.actions.SetLoadMeterPeriod.fast.text"), //$NON-NLS-1$
LOAD_METER_TIMER_FAST));
// TODO: the default load meter refresh speed is set here but we could instead rely on the value saved in the data store
SetLoadMeterPeriodAction defaultAction = new SetLoadMeterPeriodAction(
MulticoreVisualizerUIPlugin.getString("MulticoreVisualizer.actions.SetLoadMeterPeriod.medium.text"), //$NON-NLS-1$
LOAD_METER_TIMER_MEDIUM);
@ -520,7 +554,7 @@ public class MulticoreVisualizer extends GraphicCanvasVisualizer implements IPin
// show the load meter refresh speed sub-menu only
// if the load meters are enabled
m_loadMetersRefreshSubSubmenu.setVisible(m_loadMetersEnabled);
m_loadMetersRefreshSubSubmenu.setVisible(getLoadMetersEnabled());
// Enable pinning menu item when there is a current debug session
m_pinToDbgSessionAction.setEnabled(m_sessionState != null);
@ -1158,7 +1192,7 @@ public class MulticoreVisualizer extends GraphicCanvasVisualizer implements IPin
/** Invoked when getModel() request completes. */
@ConfinedToDsfExecutor("getSession().getExecutor()")
public void getVisualizerModelDone(VisualizerModel model) {
model.setLoadMetersEnabled(m_loadMetersEnabled);
model.setLoadMetersEnabled(getLoadMetersEnabled());
updateLoads(model);
model.sort();
setCanvasModel(model);
@ -1395,7 +1429,7 @@ public class MulticoreVisualizer extends GraphicCanvasVisualizer implements IPin
return;
}
// if meters not enabled, do not query backend
if (!m_loadMetersEnabled) {
if (!getLoadMetersEnabled()) {
return;
}

View file

@ -0,0 +1,202 @@
/*******************************************************************************
* Copyright (c) 2015 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
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Marc Dumais (Ericsson) - initial API and implementation (bug 460837)
*******************************************************************************/
package org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.utils;
import java.io.ByteArrayOutputStream;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.helpers.DefaultHandler;
/** encodes and decodes memento to and from different data types; list, map, String*/
public class MementoUtils {
protected static final String ROOT_ELEMENT_TAGNAME = "root_element"; //$NON-NLS-1$
protected static final String ELEMENT_TAGNAME = "elem"; //$NON-NLS-1$
protected static final String ATTRIBUTE_NAME = "value"; //$NON-NLS-1$
/** Returns a XML memento, that encodes a single String parameter */
public static String encodeStringIntoMemento(String str) {
List<String> list = new ArrayList<String>();
list.add(str);
return encodeListIntoMemento(list);
}
/** Returns a single String parameter, decoded from a XML memento */
public static String decodeStringFromMemento(String memento) {
return decodeListFromMemento(memento).get(0);
}
/** Returns a XML memento, that encodes a Map of Strings */
public static String encodeMapIntoMemento(Map<String, String> keyPairValues) {
String returnValue = null;
DocumentBuilderFactory dfactory = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = null;
try {
docBuilder = dfactory.newDocumentBuilder();
Document doc = docBuilder.newDocument();
Element rootElement = doc.createElement(ROOT_ELEMENT_TAGNAME);
doc.appendChild(rootElement);
// create one XML element per map entry
for (String key : keyPairValues.keySet()) {
Element elem = doc.createElement(ELEMENT_TAGNAME);
elem.setAttribute(key, keyPairValues.get(key));
rootElement.appendChild(elem);
}
ByteArrayOutputStream s = new ByteArrayOutputStream();
TransformerFactory factory = TransformerFactory.newInstance();
Transformer transformer = factory.newTransformer();
transformer.setOutputProperty(OutputKeys.METHOD, "xml"); //$NON-NLS-1$
transformer.setOutputProperty(OutputKeys.INDENT, "yes"); //$NON-NLS-1$
DOMSource source = new DOMSource(doc);
StreamResult outputTarget = new StreamResult(s);
transformer.transform(source, outputTarget);
returnValue = s.toString("UTF8"); //$NON-NLS-1$
} catch (Exception e) {
e.printStackTrace();
}
return returnValue;
}
/** Returns a Map of Strings, decoded from a XML memento */
public static Map<String, String> decodeMapFromMemento(String memento) {
Map<String, String> keyPairValues = new HashMap<String, String>();
Element root = null;
DocumentBuilder parser;
try {
parser = DocumentBuilderFactory.newInstance().newDocumentBuilder();
parser.setErrorHandler(new DefaultHandler());
root = parser.parse(new InputSource(new StringReader(memento))).getDocumentElement();
NodeList nodeList = root.getChildNodes();
for (int i = 0; i < nodeList.getLength(); i++) {
Node node = nodeList.item(i);
if (node.getNodeType() == Node.ELEMENT_NODE) {
Element elem = (Element) node;
NamedNodeMap nodeMap = elem.getAttributes();
for(int idx = 0; idx < nodeMap.getLength(); idx++) {
Node attrNode = nodeMap.item(idx);
if (attrNode.getNodeType() == Node.ATTRIBUTE_NODE) {
Attr attr = (Attr) attrNode;
String key = attr.getName();
String value = attr.getValue();
if (key != null && value != null) {
keyPairValues.put(key, value);
}
else {
throw new Exception();
}
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return keyPairValues;
}
/** Returns a XML memento, that encodes a List of Strings */
public static String encodeListIntoMemento(List<String> labels) {
String returnValue = null;
DocumentBuilderFactory dfactory = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = null;
try {
docBuilder = dfactory.newDocumentBuilder();
Document doc = docBuilder.newDocument();
Element rootElement = doc.createElement(ROOT_ELEMENT_TAGNAME);
doc.appendChild(rootElement);
// create one XML element per list entry to save
for (String lbl : labels) {
Element elem = doc.createElement(ELEMENT_TAGNAME);
elem.setAttribute(ATTRIBUTE_NAME, lbl);
rootElement.appendChild(elem);
}
ByteArrayOutputStream s = new ByteArrayOutputStream();
TransformerFactory factory = TransformerFactory.newInstance();
Transformer transformer = factory.newTransformer();
transformer.setOutputProperty(OutputKeys.METHOD, "xml"); //$NON-NLS-1$
transformer.setOutputProperty(OutputKeys.INDENT, "yes"); //$NON-NLS-1$
DOMSource source = new DOMSource(doc);
StreamResult outputTarget = new StreamResult(s);
transformer.transform(source, outputTarget);
returnValue = s.toString("UTF8"); //$NON-NLS-1$
} catch (Exception e) {
e.printStackTrace();
}
return returnValue;
}
/** Returns a List of Strings, decoded from a XML memento */
public static List<String> decodeListFromMemento(String memento) {
List<String> list = new ArrayList<String>();
Element root = null;
DocumentBuilder parser;
try {
parser = DocumentBuilderFactory.newInstance().newDocumentBuilder();
parser.setErrorHandler(new DefaultHandler());
root = parser.parse(new InputSource(new StringReader(memento))).getDocumentElement();
NodeList nodeList = root.getChildNodes();
for (int i = 0; i < nodeList.getLength(); i++) {
Node node = nodeList.item(i);
if (node.getNodeType() == Node.ELEMENT_NODE) {
Element elem = (Element) node;
String value = elem.getAttribute(ATTRIBUTE_NAME);
if (value != null) {
list.add(value);
}
else {
throw new Exception();
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return list;
}
}

View file

@ -0,0 +1,193 @@
/*******************************************************************************
* Copyright (c) 2015 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
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Marc Dumais (Ericsson) - initial API and implementation (bug 460837)
*******************************************************************************/
package org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.utils;
import java.security.InvalidParameterException;
import org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.ui.MulticoreVisualizerUIPlugin;
import org.eclipse.core.runtime.preferences.IEclipsePreferences;
import org.osgi.service.prefs.BackingStoreException;
/** This class manages one or more PersistentSetting objects, using a commmon
* name-space and optionally an instance id so that multiple instances can
* each have their own version of the parameter persisted */
public class PersistentSettingsManager {
// TODO: add a way to notify clients that the value of a global (shared) parameter
// has been updated, and that they should re-read it.
/** Class for a specific persistent parameter */
public class PersistentParameter<T> {
private String m_storeKey;
private T m_value;
private T m_defaultValue;
private Class<T> myClazz;
private boolean m_perInstance;
/**
* Constructor
* @param name: unique (for the namespace) label that identifies this parameter
* @param perInstance: whether the parameter's value is persisted per client instance or
* globally (one common shared stored value for all instances)
* @param storeKey : The key used to store the parameter in the store
*/
public PersistentParameter(Class<T> clazz, boolean perInstance, String storeKey) {
myClazz = clazz;
m_perInstance = perInstance;
m_storeKey = storeKey;
}
/** Sets the default value to use if no persistent
* value is found for this parameter */
public void setDefault(T defaultValue) {
m_defaultValue = defaultValue;
}
/** Sets the persistent value to set */
public void set(T value) {
m_value = value;
// save value in preference store
persistParameter(value);
}
/** Gets the persistent value, if found, else the default value */
public T value() {
if (m_value == null) {
// attempt to get the value from the preference store
m_value = restoreParameter();
}
// parameter has one value for any/all instances
else if(!m_perInstance) {
// do not rely on cached value, since another instance might have
// changed it - reread from data store
m_value = restoreParameter();
}
return (m_value!=null)? m_value : m_defaultValue;
}
/** Attempts to find the parameter in the preference store. Returns null if not found */
private T restoreParameter() {
IEclipsePreferences store = MulticoreVisualizerUIPlugin.getEclipsePreferenceStore();
String memento = store.get(m_storeKey, null);
if (memento == null) return null;
String val = MementoUtils.decodeStringFromMemento(memento);
T convertedVal = convertToT(val);
return convertedVal;
}
/** Saves parameter's value in preference store */
private void persistParameter(T value) {
// create memento
String memento = MementoUtils.encodeStringIntoMemento(value.toString());
// save memento in store
if (memento != null) {
IEclipsePreferences store = MulticoreVisualizerUIPlugin.getEclipsePreferenceStore();
store.put(m_storeKey, memento);
try {
store.flush();
} catch (BackingStoreException e) {
e.printStackTrace();
}
}
}
@SuppressWarnings("unchecked")
/** Converts the stored value from a String to its expected type */
private T convertToT(String val) {
// TODO: Add other types? Float, etc
if (String.class.isAssignableFrom(myClazz)) {
return (T) val;
}
else if (Integer.class.isAssignableFrom(myClazz)) {
return (T) Integer.valueOf(val);
}
else if (Boolean.class.isAssignableFrom(myClazz)) {
return (T) Boolean.valueOf(val);
}
return null;
}
}
/** String that is used to insulate the namespace for the parameters
* saved by a specific instance of the class */
private final String m_category;
/** an identifier that differentiates different client instances. For example, to save the
* value of a parameter that is applicable per-view, the view secondary id could be used so
* that each view has its own stored value */
private final String m_instance;
/**
* Constructor
* @param category : an optional id that is used to insulate the namespace for the parameters
* saved by this instance of the class. Using different category values permits to distinguish
* two or more parameters with the same label. Example: class name where the parameter is used.
* This can be set to null if unused.
* @param instance : a unique id that identifies the client's instance. Used when
* a parameter is defined as per-instance
*/
public PersistentSettingsManager(String category, String instance) {
m_category = category != null ? category : ""; //$NON-NLS-1$
m_instance = instance != null ? instance : ""; //$NON-NLS-1$
}
/** Constructor
* @param instance: a unique id that identifies the client's instance. Used when
* a parameter is not global (i.e. meant to be persisted per instance).
*/
public PersistentSettingsManager(String instance) {
this(null, instance);
}
/** Constructor */
public PersistentSettingsManager() {
this(null, null);
}
/**
* Creates a new persistent parameter, using the namespace and instance id of this manager.
* @param clazz: the class of the persistent parameter. Supported types: String, Integer, Boolean
* @param label: unique label that identifies this parameter.
* @param perInstance: whether the parameter's value should be persisted per client instance or
* globally (one common shared stored value for all instances)
* @param defaultValue: default value to use (mandatory)
*/
public <T> PersistentParameter<T> getNewParameter(Class<T> clazz, String label, boolean perInstance, T defaultValue) {
// check that we're dealing with one of a few supported types
// TODO: Add other types? Float, etc
if (String.class.isAssignableFrom(clazz) ||
Integer.class.isAssignableFrom(clazz) ||
Boolean.class.isAssignableFrom(clazz))
{
PersistentParameter<T> setting;
// build the final store key with category, parameter label and specific instance, if applicable
setting = new PersistentParameter<T>(clazz, perInstance, getStorageKey(perInstance) + "." + label); //$NON-NLS-1$
setting.setDefault(defaultValue);
return setting;
}
else {
throw new InvalidParameterException("Unsupported class type: " + clazz.toString()); //$NON-NLS-1$
}
}
/** Returns the key to be used to save parameter, taking into account the
* instance id, if applicable */
private String getStorageKey(boolean perInstance) {
return (perInstance ? m_instance : "") + (!m_category.equals("") ? "." + m_category : ""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
}
}