/*******************************************************************************
- * Copyright (c) 2012 Ericsson
+ * Copyright (c) 2012, 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
*
* Contributors:
* Mathieu Denis <mathieu.denis@polymtl.ca> - Initial API and implementation
+ * Alexandre Montplaisir - Port to ITmfStatistics provider
+ * Patrick Tasse - Support selection range
*******************************************************************************/
package org.eclipse.linuxtools.tmf.ui.viewers.statistics;
import java.util.List;
+import java.util.Map;
+import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.TreeViewerColumn;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerComparator;
import org.eclipse.linuxtools.tmf.core.component.TmfComponent;
-import org.eclipse.linuxtools.tmf.core.event.TmfTimeRange;
-import org.eclipse.linuxtools.tmf.core.event.TmfTimestamp;
-import org.eclipse.linuxtools.tmf.core.request.ITmfDataRequest;
-import org.eclipse.linuxtools.tmf.core.signal.TmfExperimentRangeUpdatedSignal;
-import org.eclipse.linuxtools.tmf.core.signal.TmfExperimentUpdatedSignal;
+import org.eclipse.linuxtools.tmf.core.request.ITmfEventRequest;
import org.eclipse.linuxtools.tmf.core.signal.TmfRangeSynchSignal;
import org.eclipse.linuxtools.tmf.core.signal.TmfSignalHandler;
+import org.eclipse.linuxtools.tmf.core.signal.TmfTimeSynchSignal;
+import org.eclipse.linuxtools.tmf.core.signal.TmfTraceRangeUpdatedSignal;
+import org.eclipse.linuxtools.tmf.core.statistics.ITmfStatistics;
+import org.eclipse.linuxtools.tmf.core.timestamp.ITmfTimestamp;
+import org.eclipse.linuxtools.tmf.core.timestamp.TmfTimeRange;
import org.eclipse.linuxtools.tmf.core.trace.ITmfTrace;
import org.eclipse.linuxtools.tmf.core.trace.TmfExperiment;
+import org.eclipse.linuxtools.tmf.core.trace.TmfTraceManager;
import org.eclipse.linuxtools.tmf.ui.viewers.TmfViewer;
-import org.eclipse.linuxtools.tmf.ui.viewers.statistics.model.AbsTmfStatisticsTree;
import org.eclipse.linuxtools.tmf.ui.viewers.statistics.model.ITmfColumnDataProvider;
import org.eclipse.linuxtools.tmf.ui.viewers.statistics.model.TmfBaseColumnData;
import org.eclipse.linuxtools.tmf.ui.viewers.statistics.model.TmfBaseColumnDataProvider;
-import org.eclipse.linuxtools.tmf.ui.viewers.statistics.model.TmfBaseStatisticsTree;
+import org.eclipse.linuxtools.tmf.ui.viewers.statistics.model.TmfStatisticsTree;
+import org.eclipse.linuxtools.tmf.ui.viewers.statistics.model.TmfStatisticsTreeManager;
import org.eclipse.linuxtools.tmf.ui.viewers.statistics.model.TmfStatisticsTreeNode;
-import org.eclipse.linuxtools.tmf.ui.viewers.statistics.model.TmfStatisticsTreeRootFactory;
import org.eclipse.linuxtools.tmf.ui.viewers.statistics.model.TmfTreeContentProvider;
+import org.eclipse.linuxtools.tmf.ui.views.statistics.TmfStatisticsModule;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
*/
public class TmfStatisticsViewer extends TmfViewer {
- /**
- * The initial window span (in nanoseconds)
- */
- public static final long INITIAL_WINDOW_SPAN = (1L * 100 * 1000 * 1000); // .1sec
-
/**
* Timestamp scale (nanosecond)
*/
- public static final byte TIME_SCALE = -9;
+ public static final byte TIME_SCALE = ITmfTimestamp.NANOSECOND_SCALE;
/**
* Default PAGE_SIZE for background requests.
*/
protected final Long STATS_INPUT_CHANGED_REFRESH = 5000L;
- /**
- * Stores the request to the experiment
- */
- protected TmfStatisticsRequest fRequest = null;
-
- /**
- * Stores the ranged request to the experiment
- */
- protected TmfStatisticsRequest fRequestRange = null;
-
/**
* The actual tree viewer to display
*/
/**
* The statistics tree linked to this viewer
*/
- protected AbsTmfStatisticsTree fStatisticsData;
+ protected TmfStatisticsTree fStatisticsData;
/**
* Update synchronization parameter (used for streaming): Update busy
*/
protected final Object fStatisticsRangeUpdateSyncObj = new Object();
+ /**
+ * The trace that is displayed by this viewer
+ */
+ protected ITmfTrace fTrace;
+
+ /**
+ * Stores the requested time range.
+ */
+ protected TmfTimeRange fRequestedTimerange;
+
/**
* Indicates to process all events
*/
private int fInstanceNb;
/**
- * The trace that is displayed by this viewer
- */
- private ITmfTrace fTrace;
-
- /**
- * Object to store the cursor while waiting for the experiment to load
+ * Object to store the cursor while waiting for the trace to load
*/
private Cursor fWaitCursor = null;
private int fWaitCursorCount = 0;
/**
- * Tells to send a time range request when the experiment gets updated.
+ * Tells to send a time range request when the trace gets updated.
*/
private boolean fSendRangeRequest = true;
+ /** Reference to the trace manager */
+ private final TmfTraceManager fTraceManager;
+
/**
* Empty constructor. To be used in conjunction with
* {@link TmfStatisticsViewer#init(Composite, String, ITmfTrace)}
*/
public TmfStatisticsViewer() {
super();
+ fTraceManager = TmfTraceManager.getInstance();
}
/**
*/
public TmfStatisticsViewer(Composite parent, String viewerName, ITmfTrace trace) {
init(parent, viewerName, trace);
+ fTraceManager = TmfTraceManager.getInstance();
}
/**
fInstanceNb = fCountInstance;
fTrace = trace;
- // The viewer will process all events if he is assigned to the experiment
+ // The viewer will process all events if he is assigned to an experiment
fProcessAll = (trace instanceof TmfExperiment);
initContent(parent);
initInput();
}
- /*
- * (non-Javadoc)
- *
- * @see org.eclipse.linuxtools.tmf.core.component.TmfComponent#dispose()
- */
@Override
public void dispose() {
super.dispose();
if (fWaitCursor != null) {
fWaitCursor.dispose();
}
- /*
- * Make sure there is no request running before removing the statistics
- * tree
- */
- cancelOngoingRequest(fRequestRange);
- cancelOngoingRequest(fRequest);
- // Clean the model
- TmfStatisticsTreeRootFactory.removeStatTreeRoot(getTreeID());
+ // Clean the model for this viewer
+ TmfStatisticsTreeManager.removeStatTreeRoot(getTreeID());
}
+ // ------------------------------------------------------------------------
+ // Signal handlers
+ // ------------------------------------------------------------------------
+
/**
- * Handles the signal about new experiment range.
+ * Handles the signal about new trace range.
*
* @param signal
- * The experiment range updated signal
+ * The trace range updated signal
*/
@TmfSignalHandler
- public void experimentRangeUpdated(TmfExperimentRangeUpdatedSignal signal) {
- TmfExperiment experiment = signal.getExperiment();
+ public void traceRangeUpdated(TmfTraceRangeUpdatedSignal signal) {
+ ITmfTrace trace = signal.getTrace();
// validate
- if (!experiment.equals(TmfExperiment.getCurrentExperiment())) {
+ if (!isListeningTo(trace)) {
return;
}
// Sends the time range request only once from this method.
if (fSendRangeRequest) {
fSendRangeRequest = false;
- // Calculate the selected time range to request
- long startTime = signal.getRange().getStartTime().normalize(0, TIME_SCALE).getValue();
- TmfTimestamp startTS = new TmfTimestamp(startTime, TIME_SCALE);
- TmfTimestamp endTS = new TmfTimestamp(startTime + INITIAL_WINDOW_SPAN, TIME_SCALE);
- TmfTimeRange timeRange = new TmfTimeRange(startTS, endTS);
-
- requestTimeRangeData(experiment, timeRange);
+ ITmfTimestamp begin = fTraceManager.getSelectionBeginTime();
+ ITmfTimestamp end = fTraceManager.getSelectionEndTime();
+ TmfTimeRange timeRange = new TmfTimeRange(begin, end);
+ requestTimeRangeData(trace, timeRange);
}
}
- requestData(experiment, signal.getRange());
+ requestData(trace, signal.getRange());
}
/**
- * Handles the experiment updated signal. This will detect new events in
- * case the indexing is not coalesced with a statistics request.
+ * Handles the time range updated signal. It updates the time range
+ * statistics.
*
* @param signal
- * The experiment updated signal
+ * Contains the information about the new selected time range.
+ * @deprecated
+ * As of 2.1, use {@link #timeSynchUpdated(TmfTimeSynchSignal)}
*/
+ @Deprecated
@TmfSignalHandler
- public void experimentUpdated(TmfExperimentUpdatedSignal signal) {
- TmfExperiment experiment = signal.getExperiment();
- if (!experiment.equals(TmfExperiment.getCurrentExperiment())) {
- return;
- }
-
- long nbEvents = 0;
- if (fRequest != null) {
- nbEvents = fRequest.getLastEventIndex();
- }
- /*
- * In the normal case, the statistics request is coalesced with indexing
- * and the number of events are the same, there is nothing to do. But if
- * it's not the case, trigger a new request to count the new events.
- */
- if (nbEvents < experiment.getNbEvents()) {
- requestData(experiment, experiment.getTimeRange());
- }
+ public void timeRangeUpdated(TmfRangeSynchSignal signal) {
}
/**
- * * Handles the time range updated signal. It updates the time range
+ * Handles the time synch updated signal. It updates the time range
* statistics.
*
* @param signal
* Contains the information about the new selected time range.
+ * @since 2.1
*/
@TmfSignalHandler
- public void timeRangeUpdated(TmfRangeSynchSignal signal) {
- /*
- * It is possible that the time range changes while a request is
- * processing.
- */
- cancelOngoingRequest(fRequestRange);
-
- requestTimeRangeData(TmfExperiment.getCurrentExperiment(), signal.getCurrentRange());
+ public void timeSynchUpdated(TmfTimeSynchSignal signal) {
+ if (fTrace == null) {
+ return;
+ }
+ ITmfTimestamp begin = signal.getBeginTime();
+ ITmfTimestamp end = signal.getEndTime();
+ TmfTimeRange timeRange = new TmfTimeRange(begin, end);
+ requestTimeRangeData(fTrace, timeRange);
}
+ // ------------------------------------------------------------------------
+ // Class methods
+ // ------------------------------------------------------------------------
+
/*
* Returns the primary control associated with this viewer.
*
*
* @return a TmfStatisticsData object.
*/
- public AbsTmfStatisticsTree getStatisticData() {
+ public TmfStatisticsTree getStatisticData() {
if (fStatisticsData == null) {
- fStatisticsData = new TmfBaseStatisticsTree();
+ fStatisticsData = new TmfStatisticsTree();
}
return fStatisticsData;
}
*
* @param request
* The request to be canceled
+ * @since 3.0
*/
- protected void cancelOngoingRequest(ITmfDataRequest request) {
+ protected void cancelOngoingRequest(ITmfEventRequest request) {
if (request != null && !request.isCompleted()) {
request.cancel();
}
/**
* Initializes the input for the tree viewer.
- *
- * @param input
- * The input of this viewer, or <code>null</code> if none
*/
protected void initInput() {
String treeID = getTreeID();
- TmfStatisticsTreeNode experimentTreeNode;
- if (TmfStatisticsTreeRootFactory.containsTreeRoot(treeID)) {
- // The experiment root is already present
- experimentTreeNode = TmfStatisticsTreeRootFactory.getStatTreeRoot(treeID);
+ TmfStatisticsTreeNode statisticsTreeNode;
+ if (TmfStatisticsTreeManager.containsTreeRoot(treeID)) {
+ // The statistics root is already present
+ statisticsTreeNode = TmfStatisticsTreeManager.getStatTreeRoot(treeID);
// Checks if the trace is already in the statistics tree.
- int numNodeTraces = experimentTreeNode.getNbChildren();
-
- int numTraces = 1;
- ITmfTrace[] trace = { fTrace };
- // For experiment, gets all the traces within it
- if (fTrace instanceof TmfExperiment) {
- TmfExperiment experiment = (TmfExperiment) fTrace;
- numTraces = experiment.getTraces().length;
- trace = experiment.getTraces();
- }
+ int numNodeTraces = statisticsTreeNode.getNbChildren();
+
+ ITmfTrace[] traces = TmfTraceManager.getTraceSet(fTrace);
+ int numTraces = traces.length;
if (numTraces == numNodeTraces) {
boolean same = true;
* previously selected.
*/
for (int i = 0; i < numTraces; i++) {
- String traceName = trace[i].getName();
- if (!experimentTreeNode.containsChild(traceName)) {
+ String traceName = traces[i].getName();
+ if (!statisticsTreeNode.containsChild(traceName)) {
same = false;
break;
}
if (same) {
// No need to reload data, all traces are already loaded
- fTreeViewer.setInput(experimentTreeNode);
+ fTreeViewer.setInput(statisticsTreeNode);
return;
}
// Clears the old content to start over
- experimentTreeNode.reset();
+ statisticsTreeNode.reset();
}
} else {
// Creates a new tree
- experimentTreeNode = TmfStatisticsTreeRootFactory.addStatsTreeRoot(treeID, getStatisticData());
+ statisticsTreeNode = TmfStatisticsTreeManager.addStatsTreeRoot(treeID, getStatisticData());
}
// Sets the input to a clean data model
- fTreeViewer.setInput(experimentTreeNode);
+ fTreeViewer.setInput(statisticsTreeNode);
resetUpdateSynchronization();
}
/**
- * Tells if the viewer is listening to a trace from the selected experiment.
+ * Tells if the viewer is listening to a trace.
*
- * @param traceName
+ * @param trace
* The trace that the viewer may be listening
* @return true if the viewer is listening to the trace, false otherwise
*/
- protected boolean isListeningTo(String traceName) {
- if (fProcessAll || traceName.equals(fTrace.getName())) {
+ protected boolean isListeningTo(ITmfTrace trace) {
+ if (fProcessAll || trace == fTrace) {
return true;
}
return false;
}
/**
- * Called when an experiment request has been completed successfully.
+ * Called when an trace request has been completed successfully.
*
* @param global
* Tells if the request is a global or time range (partial)
}
/**
- * Called when an experiment request has failed or has been cancelled.
+ * Called when an trace request has failed or has been cancelled.
*
* @param isGlobalRequest
* Tells if the request is a global or time range (partial)
}
/**
- * Sends the request to the experiment for the whole trace
+ * Sends the request to the trace for the whole trace
*
- * @param experiment
- * The experiment used to send the request
- * @param range
- * The range to request to the experiment
- */
- protected void requestData(TmfExperiment experiment, TmfTimeRange range) {
- // Check if an update is already ongoing
- if (checkUpdateBusy(range)) {
+ * @param trace
+ * The trace used to send the request
+ * @param timeRange
+ * The range to request to the trace
+ */
+ protected void requestData(final ITmfTrace trace, final TmfTimeRange timeRange) {
+ buildStatisticsTree(trace, timeRange, true);
+ }
+
+ /**
+ * Sends the time range request from the trace
+ *
+ * @param trace
+ * The trace used to send the request
+ * @param timeRange
+ * The range to request to the trace
+ */
+ protected void requestTimeRangeData(final ITmfTrace trace, final TmfTimeRange timeRange) {
+ fRequestedTimerange = timeRange;
+ buildStatisticsTree(trace, timeRange, false);
+ }
+
+ /**
+ * Requests all the data of the trace to the state system which
+ * contains information about the statistics.
+ *
+ * Since the viewer may be listening to multiple traces, it may receive
+ * an experiment rather than a single trace. The filtering is done with the
+ * method {@link #isListeningTo(String trace)}.
+ *
+ * @param trace
+ * The trace for which a request must be done
+ * @param timeRange
+ * The time range that will be requested to the state system
+ * @param isGlobal
+ * Tells if the request is for the global event count or the
+ * partial one.
+ */
+ private void buildStatisticsTree(final ITmfTrace trace, final TmfTimeRange timeRange, final boolean isGlobal) {
+ final TmfStatisticsTreeNode statTree = TmfStatisticsTreeManager.getStatTreeRoot(getTreeID());
+ final TmfStatisticsTree statsData = TmfStatisticsTreeManager.getStatTree(getTreeID());
+ if (statsData == null) {
return;
}
- long index = 0;
- /*
- * Sets the index to the last event retrieved from the experiment during
- * the last request.
- */
- if (fRequest != null) {
- index = fRequest.getLastEventIndex();
- }
+ synchronized (statsData) {
+ if (isGlobal) {
+ statTree.resetGlobalValue();
+ } else {
+ statTree.resetTimeRangeValue();
+ }
- fRequest = new TmfStatisticsRequest(this, range, index, true);
- waitCursor(true);
- experiment.sendRequest(fRequest);
+ for (final ITmfTrace aTrace : TmfTraceManager.getTraceSet(trace)) {
+ if (!isListeningTo(aTrace)) {
+ continue;
+ }
+
+ /* Retrieve the statistics object */
+ final TmfStatisticsModule statsMod = aTrace.getAnalysisModuleOfClass(TmfStatisticsModule.class, TmfStatisticsModule.ID);
+ if (statsMod == null) {
+ /* No statistics module available for this trace */
+ continue;
+ }
+
+ /* Run the potentially long queries in a separate thread */
+ Thread statsThread = new Thread("Statistics update") { //$NON-NLS-1$
+ @Override
+ public void run() {
+ /* Wait until the analysis is ready */
+ if (!statsMod.waitForCompletion(new NullProgressMonitor())) {
+ return;
+ }
+
+ ITmfStatistics stats = statsMod.getStatistics();
+ if (stats == null) {
+ /* It should have worked, but didn't */
+ return;
+ }
+
+ /*
+ * The generic statistics are stored in nanoseconds, so
+ * we must make sure the time range is scaled correctly.
+ */
+ long start = timeRange.getStartTime().normalize(0, TIME_SCALE).getValue();
+ long end = timeRange.getEndTime().normalize(0, TIME_SCALE).getValue();
+
+ Map<String, Long> map = stats.getEventTypesInRange(start, end);
+ updateStats(isGlobal, map);
+ }
+ };
+ statsThread.start();
+ return;
+ }
+ }
}
/**
- * Sends the time range request from the experiment
+ * Whenever a trace's statistics back-end finishes computing the statistics
+ * for a given interval, it will send the StatsUpdated signal. This method
+ * will receive this signal and update the statistics view accordingly.
*
- * @param experiment
- * The experiment used to send the request
- * @param range
- * The range to request to the experiment
+ * @param sig
+ * The signal that is received
*/
- protected void requestTimeRangeData(TmfExperiment experiment, TmfTimeRange range) {
- resetTimeRangeValue();
- fRequestRange = new TmfStatisticsRequest(this, range, 0, false);
- waitCursor(true);
- experiment.sendRequest(fRequestRange);
+ private void updateStats(boolean isGlobal, Map<String, Long> eventsPerType) {
+
+ final TmfStatisticsTree statsData = TmfStatisticsTreeManager.getStatTree(getTreeID());
+ Map<String, Long> map = eventsPerType;
+ String name = fTrace.getName();
+
+ /*
+ * "Global", "partial", "total", etc., it's all very confusing...
+ *
+ * The base view shows the total count for the trace and for
+ * each even types, organized in columns like this:
+ *
+ * | Global | Time range |
+ * trace name | A | B |
+ * Event Type | | |
+ * <event 1> | C | D |
+ * <event 2> | ... | ... |
+ * ... | | |
+ *
+ * Here, we called the cells like this:
+ * A : GlobalTotal
+ * B : TimeRangeTotal
+ * C : GlobalTypeCount(s)
+ * D : TimeRangeTypeCount(s)
+ */
+
+ /* Fill in an the event counts (either cells C or D) */
+ for (Map.Entry<String, Long> entry : map.entrySet()) {
+ statsData.setTypeCount(name, entry.getKey(), isGlobal, entry.getValue());
+ }
+
+ /*
+ * Calculate the totals (cell A or B, depending if isGlobal). We will
+ * use the results of the previous request instead of sending another
+ * one.
+ */
+ long globalTotal = 0;
+ for (long val : map.values()) {
+ globalTotal += val;
+ }
+ statsData.setTotal(name, isGlobal, globalTotal);
+
+ modelComplete(isGlobal);
}
/**
* Resets the number of events within the time range
*/
protected void resetTimeRangeValue() {
- TmfStatisticsTreeNode treeModelRoot = TmfStatisticsTreeRootFactory.getStatTreeRoot(getTreeID());
+ TmfStatisticsTreeNode treeModelRoot = TmfStatisticsTreeManager.getStatTreeRoot(getTreeID());
if (treeModelRoot != null && treeModelRoot.hasChildren()) {
treeModelRoot.resetTimeRangeValue();
}
}
/**
- * When the experiment is loading the cursor will be different so the user
+ * When the trace is loading the cursor will be different so the user
* knows that the processing is not finished yet.
*
* Calls to this method are stacked.
fStatisticsUpdateBusy = false;
if (fStatisticsUpdatePending) {
fStatisticsUpdatePending = false;
- requestData(TmfExperiment.getCurrentExperiment(), fStatisticsUpdateRange);
+ requestData(fTrace, fStatisticsUpdateRange);
fStatisticsUpdateRange = null;
}
}