--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2012 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:
+ * Alexandre Montplaisir - Initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.linuxtools.tmf.core.signal;
+
+import java.util.Map;
+
+import org.eclipse.linuxtools.tmf.core.statistics.ITmfStatistics;
+import org.eclipse.linuxtools.tmf.core.trace.ITmfTrace;
+
+/**
+ * Signal sent when a call to {@link ITmfStatistics#updateStats} has completed.
+ *
+ * @author Alexandre Montplaisir
+ * @version 1.0
+ * @since 2.0
+ */
+public class TmfStatsUpdatedSignal extends TmfSignal {
+
+ private final ITmfTrace trace;
+ private final boolean isGlobal;
+ private final long eventTotal;
+ private final Map<String, Long> eventsPerType;
+
+ /**
+ * Constructor
+ *
+ * @param source
+ * Object sending this signal
+ * @param trace
+ * The trace for which we track the statistics
+ * @param isGlobal
+ * Is this a global query (whole range of the trace), or not
+ * @param eventTotal
+ * The total number of events. This should be equal to the sum of
+ * the values in eventsPerType.
+ * @param eventsPerType
+ * The map representing the number of events of each type
+ *
+ * @since 2.0
+ */
+ public TmfStatsUpdatedSignal(Object source, ITmfTrace trace, boolean isGlobal,
+ long eventTotal, Map<String, Long> eventsPerType) {
+ super(source);
+ this.trace = trace;
+ this.isGlobal = isGlobal;
+ this.eventTotal = eventTotal;
+ this.eventsPerType = eventsPerType;
+ }
+
+ /**
+ * @return The trace referred to by this signal
+ */
+ public ITmfTrace getTrace() {
+ return trace;
+ }
+
+ /**
+ * @return True if it's a global query, false if it's for the current time
+ * range
+ */
+ public boolean isGlobal() {
+ return isGlobal;
+ }
+
+ /**
+ * @return The total number of events for this query
+ */
+ public long getEventTotal() {
+ return eventTotal;
+ }
+
+ /**
+ * @return The map representing the number of events per type for this query
+ */
+ public Map<String, Long> getEventsPerType() {
+ return eventsPerType;
+ }
+
+ @Override
+ public String toString() {
+ return "[TmfStatsUpdatedSignal (trace = " + trace.toString() + //$NON-NLS-1$
+ ", total = " + eventTotal + ")]"; //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+}
import java.util.Map;
import org.eclipse.linuxtools.tmf.core.event.ITmfTimestamp;
+import org.eclipse.linuxtools.tmf.core.signal.TmfStatsUpdatedSignal;
/**
* Provider for statistics, which is assigned to a trace. This can be used to
*/
public interface ITmfStatistics {
+ /**
+ * This method provides a centralized and asynchronous way of querying
+ * statistics information. It is an alternative to the other get* methods,
+ * and should not block the caller for too long.
+ *
+ * Implementors can usually call their own getEventTotal(),
+ * getEventsInRange(), etc. but should do so in a separate thread, and
+ * should send a {@link TmfStatsUpdatedSignal} whenever they are done (that
+ * signal will carry the results).
+ *
+ * @param isGlobal
+ * Is this for a global query (whole time range of a trace), or
+ * just for a specific time range.
+ * @param start
+ * The start time of the query range. Has no effect if isGlobal
+ * is true.
+ * @param end
+ * The end time of the query range. Has no effect if isGlobal is
+ * true.
+ */
+ public void updateStats(final boolean isGlobal, ITmfTimestamp start,
+ ITmfTimestamp end);
+
/**
* Return the total number of events in the trace.
*
import org.eclipse.linuxtools.tmf.core.exceptions.TimeRangeException;
import org.eclipse.linuxtools.tmf.core.statesystem.AbstractStateChangeInput;
import org.eclipse.linuxtools.tmf.core.statesystem.ITmfStateSystemBuilder;
-import org.eclipse.linuxtools.tmf.core.statistics.TmfStatistics.Attributes;
+import org.eclipse.linuxtools.tmf.core.statistics.TmfStateStatistics.Attributes;
import org.eclipse.linuxtools.tmf.core.trace.ITmfTrace;
/**
- * The state provider for traces statistics. It should work with any type of
- * trace TMF can handle.
+ * The state provider for traces statistics that use TmfStateStatistics. It
+ * should work with any trace type for which we can use the state system.
*
* The resulting attribute tree will look like this:
*
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2012 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:
+ * Alexandre Montplaisir - Initial API and implementation
+ ******************************************************************************/
+
+package org.eclipse.linuxtools.tmf.core.statistics;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.linuxtools.tmf.core.event.ITmfEvent;
+import org.eclipse.linuxtools.tmf.core.event.ITmfTimestamp;
+import org.eclipse.linuxtools.tmf.core.event.TmfTimeRange;
+import org.eclipse.linuxtools.tmf.core.request.ITmfDataRequest;
+import org.eclipse.linuxtools.tmf.core.request.TmfDataRequest;
+import org.eclipse.linuxtools.tmf.core.request.TmfEventRequest;
+import org.eclipse.linuxtools.tmf.core.signal.TmfSignal;
+import org.eclipse.linuxtools.tmf.core.signal.TmfSignalManager;
+import org.eclipse.linuxtools.tmf.core.signal.TmfStatsUpdatedSignal;
+import org.eclipse.linuxtools.tmf.core.trace.ITmfTrace;
+
+/**
+ * Implementation of ITmfStatistics which uses event requests to the trace to
+ * retrieve its information.
+ *
+ * There is almost no setup time, but queries themselves are longer than with a
+ * TmfStateStatistics. Queries are O(n * m), where n is the size of the trace,
+ * and m is the portion of the trace covered by the selected interval.
+ *
+ * @author Alexandre Montplaisir
+ * @since 2.0
+ */
+public class TmfEventsStatistics implements ITmfStatistics {
+
+ private final ITmfTrace trace;
+
+ /* Event request objects for the time-range request. */
+ private StatsTotalRequest totalRequest = null;
+ private StatsPerTypeRequest perTypeRequest = null;
+
+ /**
+ * Constructor
+ *
+ * @param trace
+ * The trace for which we are building the statistics
+ */
+ public TmfEventsStatistics(ITmfTrace trace) {
+ this.trace = trace;
+ }
+
+ @Override
+ public void updateStats(final boolean isGlobal, ITmfTimestamp start,
+ ITmfTimestamp end) {
+ cancelOngoingRequests();
+
+ /*
+ * Prepare and send the event requests. This needs to be done in the
+ * same thread, since it will be run by TmfStatisticsViewer's signal
+ * handlers, to ensure they get correctly coalesced.
+ */
+ TmfTimeRange range = isGlobal ? TmfTimeRange.ETERNITY : new TmfTimeRange(start, end);
+ final StatsTotalRequest totalReq = new StatsTotalRequest(trace, range);
+ final StatsPerTypeRequest perTypeReq = new StatsPerTypeRequest(trace, range);
+
+ /*
+ * Only allow one time-range request at a time (there should be only one
+ * global request at the beginning anyway, no need to track those).
+ */
+ if (!isGlobal) {
+ this.totalRequest = totalReq;
+ this.perTypeRequest = perTypeReq;
+ }
+
+ trace.sendRequest(totalReq);
+ trace.sendRequest(perTypeReq);
+
+ /*
+ * This thread can now return. Start a new thread that will wait until
+ * the request are done and will then send the results.
+ */
+ Thread statsThread = new Thread("Statistics update") { //$NON-NLS-1$
+ @Override
+ public void run() {
+ /* Wait for both requests to complete */
+ try {
+ totalReq.waitForCompletion();
+ perTypeReq.waitForCompletion();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+
+ /*
+ * If the request was cancelled, this means a newer one was
+ * sent, discard the current one and return without sending
+ * the signal.
+ */
+ if (totalReq.isCancelled() || perTypeReq.isCancelled()) {
+ return;
+ }
+
+ /* If it completed successfully, retrieve the results. */
+ long total = totalReq.getResult();
+ Map<String, Long> map = perTypeReq.getResults();
+
+ /* Send the signal to notify the stats viewer to update its display. */
+ TmfSignal sig = new TmfStatsUpdatedSignal(this, trace, isGlobal, total, map);
+ TmfSignalManager.dispatchSignal(sig);
+ }
+ };
+ statsThread.start();
+ return;
+ }
+
+ private synchronized void cancelOngoingRequests() {
+ if (totalRequest != null && totalRequest.isRunning()) {
+ totalRequest.cancel();
+ }
+ if (perTypeRequest != null && perTypeRequest.isRunning()) {
+ perTypeRequest.cancel();
+ }
+ }
+
+ @Override
+ public long getEventsTotal() {
+ StatsTotalRequest request = new StatsTotalRequest(trace, TmfTimeRange.ETERNITY);
+ sendAndWait(request);
+
+ long total = request.getResult();
+ return total;
+ }
+
+ @Override
+ public Map<String, Long> getEventTypesTotal() {
+ StatsPerTypeRequest request = new StatsPerTypeRequest(trace, TmfTimeRange.ETERNITY);
+ sendAndWait(request);
+
+ Map<String, Long> stats = request.getResults();
+ return stats;
+ }
+
+ @Override
+ public long getEventsInRange(ITmfTimestamp start, ITmfTimestamp end) {
+ TmfTimeRange range = new TmfTimeRange(start, end);
+ StatsTotalRequest request = new StatsTotalRequest(trace, range);
+ sendAndWait(request);
+
+ long total = request.getResult();
+ return total;
+ }
+
+ @Override
+ public Map<String, Long> getEventTypesInRange(ITmfTimestamp start,
+ ITmfTimestamp end) {
+ TmfTimeRange range = new TmfTimeRange(start, end);
+ StatsPerTypeRequest request = new StatsPerTypeRequest(trace, range);
+ sendAndWait(request);
+
+ Map<String, Long> stats = request.getResults();
+ return stats;
+ }
+
+ private void sendAndWait(TmfEventRequest request) {
+ trace.sendRequest(request);
+ try {
+ request.waitForCompletion();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+
+
+ /**
+ * Event request to get the total number of events
+ */
+ private class StatsTotalRequest extends TmfEventRequest {
+
+ /* Total number of events the request has found */
+ private long total;
+
+ public StatsTotalRequest(ITmfTrace trace, TmfTimeRange range) {
+ super(trace.getEventType(), range, TmfDataRequest.ALL_DATA,
+ trace.getCacheSize(), ITmfDataRequest.ExecutionType.BACKGROUND);
+ total = 0;
+ }
+
+ public long getResult() {
+ return total;
+ }
+
+ @Override
+ public void handleData(final ITmfEvent event) {
+ super.handleData(event);
+ if (event != null) {
+ if (event.getTrace() == trace) {
+ total += 1;
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Event request to get the counts per event type
+ */
+ private class StatsPerTypeRequest extends TmfEventRequest {
+
+ /* Map in which the results are saved */
+ private final Map<String, Long> stats;
+
+ public StatsPerTypeRequest(ITmfTrace trace, TmfTimeRange range) {
+ super(trace.getEventType(), range, TmfDataRequest.ALL_DATA,
+ trace.getCacheSize(), ITmfDataRequest.ExecutionType.BACKGROUND);
+ this.stats = new HashMap<String, Long>();
+ }
+
+ public Map<String, Long> getResults() {
+ return stats;
+ }
+
+ @Override
+ public void handleData(final ITmfEvent event) {
+ super.handleData(event);
+ if (event != null) {
+ if (event.getTrace() == trace) {
+ processEvent(event);
+ }
+ }
+ }
+
+ private void processEvent(ITmfEvent event) {
+ String eventType = event.getType().getName();
+ if (stats.containsKey(eventType)) {
+ long curValue = stats.get(eventType);
+ stats.put(eventType, curValue + 1L);
+ } else {
+ stats.put(eventType, 1L);
+ }
+ }
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2012 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:
+ * Alexandre Montplaisir - Initial API and implementation
+ ******************************************************************************/
+
+package org.eclipse.linuxtools.tmf.core.statistics;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.linuxtools.tmf.core.TmfCommonConstants;
+import org.eclipse.linuxtools.tmf.core.event.ITmfTimestamp;
+import org.eclipse.linuxtools.tmf.core.exceptions.AttributeNotFoundException;
+import org.eclipse.linuxtools.tmf.core.exceptions.StateValueTypeException;
+import org.eclipse.linuxtools.tmf.core.exceptions.TimeRangeException;
+import org.eclipse.linuxtools.tmf.core.exceptions.TmfTraceException;
+import org.eclipse.linuxtools.tmf.core.interval.ITmfStateInterval;
+import org.eclipse.linuxtools.tmf.core.signal.TmfSignal;
+import org.eclipse.linuxtools.tmf.core.signal.TmfSignalManager;
+import org.eclipse.linuxtools.tmf.core.signal.TmfStatsUpdatedSignal;
+import org.eclipse.linuxtools.tmf.core.statesystem.IStateChangeInput;
+import org.eclipse.linuxtools.tmf.core.statesystem.ITmfStateSystem;
+import org.eclipse.linuxtools.tmf.core.statesystem.StateSystemManager;
+import org.eclipse.linuxtools.tmf.core.trace.ITmfTrace;
+
+/**
+ * Implementation of ITmfStatistics which uses a state history for storing its
+ * information.
+ *
+ * It requires building the history first, but gives very fast response times
+ * when built : Queries are O(log n) wrt the size of the trace, and O(1) wrt to
+ * the size of the time interval selected.
+ *
+ * @author Alexandre Montplaisir
+ * @since 2.0
+ */
+
+public class TmfStateStatistics implements ITmfStatistics {
+
+ /** ID for the statistics state system */
+ public static final String STATE_ID = "org.eclipse.linuxtools.tmf.statistics"; //$NON-NLS-1$
+
+ /** Filename the "statistics state history" file will have */
+ private static final String STATS_STATE_FILENAME = "statistics.ht"; //$NON-NLS-1$
+
+ private final ITmfTrace trace;
+
+ /**
+ * The state system that's used to stored the statistics. It's hidden from
+ * the trace, so that it doesn't conflict with ITmfTrace.getStateSystem()
+ * (which is something else!)
+ */
+ private final ITmfStateSystem stats;
+
+ /**
+ * Empty constructor. The resulting TmfStatistics object will not be usable,
+ * but it might be needed for sub-classes.
+ */
+ public TmfStateStatistics() {
+ stats = null;
+ trace = null;
+ }
+
+ /**
+ * Constructor
+ *
+ * @param trace
+ * The trace for which we build these statistics
+ * @throws TmfTraceException
+ * If something went wrong trying to initialize the statistics
+ */
+ public TmfStateStatistics(ITmfTrace trace) throws TmfTraceException {
+ /* Set up the path to the history tree file we'll use */
+ this.trace = trace;
+ IResource resource = trace.getResource();
+ String supplDirectory = null;
+
+ try {
+ // get the directory where the history file will be stored.
+ supplDirectory = resource.getPersistentProperty(TmfCommonConstants.TRACE_SUPPLEMENTARY_FOLDER);
+ } catch (CoreException e) {
+ throw new TmfTraceException(e.toString(), e);
+ }
+
+ final File htFile = new File(supplDirectory + File.separator + STATS_STATE_FILENAME);
+ final IStateChangeInput htInput = new StatsStateProvider(trace);
+
+ this.stats = StateSystemManager.loadStateHistory(htFile, htInput, STATE_ID, false);
+ }
+
+ // ------------------------------------------------------------------------
+ // ITmfStatistics
+ // ------------------------------------------------------------------------
+
+ @Override
+ public void updateStats(final boolean isGlobal, final ITmfTimestamp start,
+ final ITmfTimestamp end) {
+ /*
+ * Since we are currently in a signal handler (ie, in the UI thread),
+ * and since state system queries can be arbitrarily long (O(log n) wrt
+ * the size of the trace), we will run those queries in a separate
+ * thread and update the statistics view out-of-band.
+ */
+ Thread statsThread = new Thread("Statistics update") { //$NON-NLS-1$
+ @Override
+ public void run() {
+ long total;
+ Map<String, Long> map;
+
+ /* Wait until the history building completed */
+ stats.waitUntilBuilt();
+
+ /* Range should be valid for both global and time range queries */
+ total = getEventsInRange(start, end);
+ map = getEventTypesInRange(start, end);
+
+ /* Send the signal to notify the stats viewer to update its display */
+ TmfSignal sig = new TmfStatsUpdatedSignal(this, trace, isGlobal, total, map);
+ TmfSignalManager.dispatchSignal(sig);
+ }
+ };
+ statsThread.start();
+ return;
+
+ }
+
+ @Override
+ public long getEventsTotal() {
+ long startTime = stats.getStartTime();
+ long endTime = stats.getCurrentEndTime();
+ int countAtStart = 0, countAtEnd = 0;
+
+ try {
+ final int quark = stats.getQuarkAbsolute(Attributes.TOTAL);
+ countAtStart = stats.querySingleState(startTime, quark).getStateValue().unboxInt();
+ countAtEnd = stats.querySingleState(endTime, quark).getStateValue().unboxInt();
+
+ } catch (TimeRangeException e) {
+ /* Assume there is no events for that range */
+ return 0;
+ } catch (AttributeNotFoundException e) {
+ e.printStackTrace();
+ } catch (StateValueTypeException e) {
+ e.printStackTrace();
+ }
+
+ long total = countAtEnd - countAtStart;
+ return total;
+ }
+
+ @Override
+ public Map<String, Long> getEventTypesTotal() {
+ Map<String, Long> map = new HashMap<String, Long>();
+ long endTime = stats.getCurrentEndTime(); //shouldn't need to check it...
+
+ try {
+ /* Get the list of quarks, one for each even type in the database */
+ int quark = stats.getQuarkAbsolute(Attributes.EVENT_TYPES);
+ List<Integer> quarks = stats.getSubAttributes(quark, false);
+
+ /* Since we want the total we can look only at the end */
+ List<ITmfStateInterval> endState = stats.queryFullState(endTime);
+
+ String curEventName;
+ long eventCount;
+ for (int typeQuark : quarks) {
+ curEventName = stats.getAttributeName(typeQuark);
+ eventCount = endState.get(typeQuark).getStateValue().unboxInt();
+ map.put(curEventName, eventCount);
+ }
+
+ } catch (TimeRangeException e) {
+ /* Assume there is no events, nothing will be put in the map. */
+ } catch (AttributeNotFoundException e) {
+ e.printStackTrace();
+ } catch (StateValueTypeException e) {
+ e.printStackTrace();
+ }
+ return map;
+ }
+
+ @Override
+ public long getEventsInRange(ITmfTimestamp start, ITmfTimestamp end) {
+ int countAtStart = 0, countAtEnd = 0;
+ long startTime = checkStartTime(start);
+ long endTime = checkEndTime(end);
+
+ try {
+ final int quark = stats.getQuarkAbsolute(Attributes.TOTAL);
+ countAtStart = stats.querySingleState(startTime, quark).getStateValue().unboxInt();
+ countAtEnd = stats.querySingleState(endTime, quark).getStateValue().unboxInt();
+
+ } catch (TimeRangeException e) {
+ /* Assume there is no events for that range */
+ return 0;
+ } catch (AttributeNotFoundException e) {
+ e.printStackTrace();
+ } catch (StateValueTypeException e) {
+ e.printStackTrace();
+ }
+
+ long total = countAtEnd - countAtStart;
+ return total;
+ }
+
+ @Override
+ public Map<String, Long> getEventTypesInRange(ITmfTimestamp start, ITmfTimestamp end) {
+ Map<String, Long> map = new HashMap<String, Long>();
+
+ /* Make sure the start/end times are within the state history, so we
+ * don't get TimeRange exceptions.
+ */
+ long startTime = checkStartTime(start);
+ long endTime = checkEndTime(end);
+
+ try {
+ /* Get the list of quarks, one for each even type in the database */
+ int quark = stats.getQuarkAbsolute(Attributes.EVENT_TYPES);
+ List<Integer> quarks = stats.getSubAttributes(quark, false);
+
+ /*
+ * Get the complete states (in our case, event counts) at the start
+ * time and end time of the requested time range.
+ */
+ List<ITmfStateInterval> startState = stats.queryFullState(startTime);
+ List<ITmfStateInterval> endState = stats.queryFullState(endTime);
+
+ /* Save the relevant information in the map we will be returning */
+ String curEventName;
+ long countAtStart, countAtEnd, eventCount;
+ for (int typeQuark : quarks) {
+ curEventName = stats.getAttributeName(typeQuark);
+ countAtStart = startState.get(typeQuark).getStateValue().unboxInt();
+ countAtEnd = endState.get(typeQuark).getStateValue().unboxInt();
+
+ /*
+ * The default value for the statistics is 0, rather than the
+ * value -1 used by the state system for non-initialized state.
+ */
+ if (startTime == stats.getStartTime() || countAtStart == -1) {
+ countAtStart = 0;
+ }
+
+ /*
+ * Workaround a bug in the state system where requests for the
+ * very last state change will give -1. Send the request 1ns
+ * before the end of the trace and add the last event to the
+ * count.
+ */
+ if (countAtEnd < 0) {
+ ITmfStateInterval realInterval = stats.querySingleState(endTime - 1, typeQuark);
+ countAtEnd = realInterval.getStateValue().unboxInt() + 1;
+ }
+
+ /*
+ * If after this it is still at -1, it's because no event of
+ * this type happened during the requested time range.
+ */
+ if (countAtEnd < 0) {
+ countAtEnd = 0;
+ }
+
+ eventCount = countAtEnd - countAtStart;
+ map.put(curEventName, eventCount);
+ }
+ } catch (TimeRangeException e) {
+ /* Assume there is no events, nothing will be put in the map. */
+ } catch (AttributeNotFoundException e) {
+ /*
+ * These other exception types would show a logic problem however,
+ * so they should not happen.
+ */
+ e.printStackTrace();
+ } catch (StateValueTypeException e) {
+ e.printStackTrace();
+ }
+ return map;
+ }
+
+ protected long checkStartTime(ITmfTimestamp startTs) {
+ long start = startTs.normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue();
+ if (start < stats.getStartTime()) {
+ return stats.getStartTime();
+ }
+ return start;
+ }
+
+ protected long checkEndTime(ITmfTimestamp endTs) {
+ long end = endTs.normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue();
+ if (end > stats.getCurrentEndTime()) {
+ return stats.getCurrentEndTime();
+ }
+ return end;
+ }
+
+
+ /**
+ * The attribute names that are used in the state provider
+ */
+ public static class Attributes {
+
+ /** Total nb of events */
+ public static final String TOTAL = "total"; //$NON-NLS-1$
+
+ /** event_types */
+ public static final String EVENT_TYPES = "event_types"; //$NON-NLS-1$<
+ }
+}
+++ /dev/null
-/*******************************************************************************
- * Copyright (c) 2012 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:
- * Alexandre Montplaisir - Initial API and implementation
- ******************************************************************************/
-
-package org.eclipse.linuxtools.tmf.core.statistics;
-
-import java.io.File;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import org.eclipse.core.resources.IResource;
-import org.eclipse.core.runtime.CoreException;
-import org.eclipse.linuxtools.tmf.core.TmfCommonConstants;
-import org.eclipse.linuxtools.tmf.core.event.ITmfTimestamp;
-import org.eclipse.linuxtools.tmf.core.exceptions.AttributeNotFoundException;
-import org.eclipse.linuxtools.tmf.core.exceptions.StateValueTypeException;
-import org.eclipse.linuxtools.tmf.core.exceptions.TimeRangeException;
-import org.eclipse.linuxtools.tmf.core.exceptions.TmfTraceException;
-import org.eclipse.linuxtools.tmf.core.interval.ITmfStateInterval;
-import org.eclipse.linuxtools.tmf.core.statesystem.IStateChangeInput;
-import org.eclipse.linuxtools.tmf.core.statesystem.ITmfStateSystem;
-import org.eclipse.linuxtools.tmf.core.statesystem.StateSystemManager;
-import org.eclipse.linuxtools.tmf.core.trace.ITmfTrace;
-
-/**
- * Default implementation of an ITmfStatisticsProvider. It uses a state system
- * underneath to store its information.
- *
- * @author Alexandre Montplaisir
- * @since 2.0
- */
-
-public class TmfStatistics implements ITmfStatistics {
-
- /** ID for the statistics state system */
- public static final String STATE_ID = "org.eclipse.linuxtools.tmf.statistics"; //$NON-NLS-1$
-
- /* Filename the "statistics state history" file will have */
- private static final String STATS_STATE_FILENAME = "statistics.ht"; //$NON-NLS-1$
-
- /*
- * The state system that's used to stored the statistics. It's hidden from
- * the trace, so that it doesn't conflict with ITmfTrace.getStateSystem()
- * (which is something else!)
- */
- private final ITmfStateSystem stats;
-
- /**
- * Empty constructor. The resulting TmfStatistics object will not be usable,
- * but it might be needed for sub-classes.
- */
- public TmfStatistics() {
- stats = null;
- }
-
- /**
- * Constructor
- *
- * @param trace
- * The trace for which we build these statistics
- * @throws TmfTraceException
- * If something went wrong trying to initialize the statistics
- */
- public TmfStatistics(ITmfTrace trace) throws TmfTraceException {
- /* Set up the path to the history tree file we'll use */
- IResource resource = trace.getResource();
- String supplDirectory = null;
-
- try {
- // get the directory where the history file will be stored.
- supplDirectory = resource.getPersistentProperty(TmfCommonConstants.TRACE_SUPPLEMENTARY_FOLDER);
- } catch (CoreException e) {
- throw new TmfTraceException(e.toString(), e);
- }
-
- final File htFile = new File(supplDirectory + File.separator + STATS_STATE_FILENAME);
- final IStateChangeInput htInput = new StatsStateProvider(trace);
-
- this.stats = StateSystemManager.loadStateHistory(htFile, htInput, STATE_ID, false);
- }
-
- // ------------------------------------------------------------------------
- // ITmfStatisticsProvider
- // ------------------------------------------------------------------------
-
- @Override
- public long getEventsTotal() {
- long start = stats.getStartTime();
- long end = stats.getCurrentEndTime();
- int countAtStart = 0, countAtEnd = 0;
-
- try {
- final int quark = stats.getQuarkAbsolute(Attributes.TOTAL);
- countAtStart = stats.querySingleState(start, quark).getStateValue().unboxInt();
- countAtEnd = stats.querySingleState(end, quark).getStateValue().unboxInt();
- } catch (AttributeNotFoundException e) {
- e.printStackTrace();
- } catch (StateValueTypeException e) {
- e.printStackTrace();
- } catch (TimeRangeException e) {
- /* Should not happen, we're clamped to the start and end times */
- e.printStackTrace();
- }
-
- long total = countAtEnd - countAtStart;
- return total;
- }
-
- @Override
- public Map<String, Long> getEventTypesTotal() {
- Map<String, Long> map = new HashMap<String, Long>();
- long endTime = stats.getCurrentEndTime(); //shouldn't need to check it...
-
- try {
- /* Get the list of quarks, one for each even type in the database */
- int quark = stats.getQuarkAbsolute(Attributes.EVENT_TYPES);
- List<Integer> quarks = stats.getSubAttributes(quark, false);
-
- /* Since we want the total we can look only at the end */
- List<ITmfStateInterval> endState = stats.queryFullState(endTime);
-
- String curEventName;
- long eventCount;
- for (int typeQuark : quarks) {
- curEventName = stats.getAttributeName(typeQuark);
- eventCount = endState.get(typeQuark).getStateValue().unboxInt();
- map.put(curEventName, eventCount);
- }
-
- } catch (TimeRangeException e) {
- /* Ignore silently */
- } catch (AttributeNotFoundException e) {
- e.printStackTrace();
- } catch (StateValueTypeException e) {
- e.printStackTrace();
- }
- return map;
- }
-
- @Override
- public long getEventsInRange(ITmfTimestamp start, ITmfTimestamp end) {
- int countAtStart = 0, countAtEnd = 0;
- long startTimestamp = checkStartTime(start.getValue());
- long endTimestamp = checkEndTime(end.getValue());
-
- try {
- final int quark = stats.getQuarkAbsolute(Attributes.TOTAL);
- countAtStart = stats.querySingleState(startTimestamp, quark).getStateValue().unboxInt();
- countAtEnd = stats.querySingleState(endTimestamp, quark).getStateValue().unboxInt();
- } catch (AttributeNotFoundException e) {
- e.printStackTrace();
- } catch (StateValueTypeException e) {
- e.printStackTrace();
- } catch (TimeRangeException e) {
- /* Should not happen, we're clamped to the start and end times */
- e.printStackTrace();
- }
-
- long total = countAtEnd - countAtStart;
- return total;
- }
-
- @Override
- public Map<String, Long> getEventTypesInRange(ITmfTimestamp start, ITmfTimestamp end) {
- Map<String, Long> map = new HashMap<String, Long>();
-
- /* Make sure the start/end times are within the state history, so we
- * don't get TimeRange exceptions.
- */
- long startTimestamp = checkStartTime(start.getValue());
- long endTimestamp = checkEndTime(end.getValue());
-
- try {
- /* Get the list of quarks, one for each even type in the database */
- int quark = stats.getQuarkAbsolute(Attributes.EVENT_TYPES);
- List<Integer> quarks = stats.getSubAttributes(quark, false);
-
- /*
- * Get the complete states (in our case, event counts) at the start
- * time and end time of the requested time range.
- */
- List<ITmfStateInterval> startState = stats.queryFullState(startTimestamp);
- List<ITmfStateInterval> endState = stats.queryFullState(endTimestamp);
-
- /* Save the relevant information in the map we will be returning */
- String curEventName;
- long countAtStart, countAtEnd, eventCount;
- for (int typeQuark : quarks) {
- curEventName = stats.getAttributeName(typeQuark);
- countAtStart = startState.get(typeQuark).getStateValue().unboxInt();
- countAtEnd = endState.get(typeQuark).getStateValue().unboxInt();
-
- /*
- * The default value for the statistics is 0, rather than the
- * value -1 used by the state system for non-initialized state.
- */
- if (startTimestamp == stats.getStartTime() || countAtStart == -1) {
- countAtStart = 0;
- }
-
- /*
- * Workaround a bug in the state system where requests for the
- * very last state change will give -1. Send the request 1ns
- * before the end of the trace and add the last event to the
- * count.
- */
- if (countAtEnd < 0) {
- ITmfStateInterval realInterval = stats.querySingleState(endTimestamp - 1, typeQuark);
- countAtEnd = realInterval.getStateValue().unboxInt() + 1;
- }
-
- /*
- * If after this it is still at -1, it's because no event of
- * this type happened during the requested time range.
- */
- if (countAtEnd < 0) {
- countAtEnd = 0;
- }
-
- eventCount = countAtEnd - countAtStart;
- map.put(curEventName, eventCount);
- }
- } catch (TimeRangeException e) {
- /*
- * If a request is made for an invalid time range, we will ignore it
- * silently and not add any information to the map.
- */
- } catch (AttributeNotFoundException e) {
- /*
- * These other exceptions would show a logic problem however, so
- * they should not happen.
- */
- e.printStackTrace();
- } catch (StateValueTypeException e) {
- e.printStackTrace();
- }
- return map;
- }
-
- protected long checkStartTime(long start) {
- if (start < stats.getStartTime()) {
- return stats.getStartTime();
- }
- return start;
- }
-
- protected long checkEndTime(long end) {
- if (end > stats.getCurrentEndTime()) {
- return stats.getCurrentEndTime();
- }
- return end;
- }
-
-
- /**
- * The attribute names that are used in the state provider
- */
- public static class Attributes {
-
- /** Total nb of events */
- public static final String TOTAL = "total"; //$NON-NLS-1$
-
- /** event_types */
- public static final String EVENT_TYPES = "event_types"; //$NON-NLS-1$<
- }
-}
import org.eclipse.linuxtools.tmf.core.request.ITmfEventRequest;
import org.eclipse.linuxtools.tmf.core.statesystem.ITmfStateSystem;
import org.eclipse.linuxtools.tmf.core.statistics.ITmfStatistics;
-import org.eclipse.linuxtools.tmf.core.statistics.TmfStatistics;
+import org.eclipse.linuxtools.tmf.core.statistics.TmfStateStatistics;
/**
* Abstract implementation of ITmfTrace.
* Initialize the statistics provider, but only if a Resource has been
* set (so we don't build it for experiments, for unit tests, etc.)
*/
- fStatistics = (fResource == null ? null : new TmfStatistics(this) );
+ fStatistics = (fResource == null ? null : new TmfStateStatistics(this) );
}
/**
import org.eclipse.linuxtools.tmf.core.signal.TmfRangeSynchSignal;
import org.eclipse.linuxtools.tmf.core.signal.TmfSignalHandler;
import org.eclipse.linuxtools.tmf.core.signal.TmfStateSystemBuildCompleted;
+import org.eclipse.linuxtools.tmf.core.signal.TmfStatsUpdatedSignal;
import org.eclipse.linuxtools.tmf.core.statistics.ITmfStatistics;
-import org.eclipse.linuxtools.tmf.core.statistics.TmfStatistics;
+import org.eclipse.linuxtools.tmf.core.statistics.TmfStateStatistics;
import org.eclipse.linuxtools.tmf.core.trace.ITmfTrace;
import org.eclipse.linuxtools.tmf.core.trace.TmfExperiment;
import org.eclipse.linuxtools.tmf.ui.viewers.TmfViewer;
TmfStatisticsTreeManager.removeStatTreeRoot(getTreeID());
}
- /**
- * Handler for the state system build completed signal
- *
- * @param signal
- * The signal that's received
- */
- @TmfSignalHandler
- public void stateSystemBuildCompleted(final TmfStateSystemBuildCompleted signal) {
- if (isListeningTo(signal.getTrace().getName()) && signal.getID().equals(TmfStatistics.STATE_ID)) {
- TmfExperiment experiment = TmfExperiment.getCurrentExperiment();
- requestData(experiment, experiment.getTimeRange());
- requestTimeRangeData(experiment, fRequestedTimerange);
- }
- }
+ // ------------------------------------------------------------------------
+ // Signal handlers
+ // ------------------------------------------------------------------------
/**
* Handles the signal about new experiment range.
requestTimeRangeData(TmfExperiment.getCurrentExperiment(), signal.getCurrentRange());
}
+ /**
+ * 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 sig
+ * The signal that is received
+ */
+ @TmfSignalHandler
+ public void statsUpdated(TmfStatsUpdatedSignal sig) {
+ /* Only handle this signal if it's about the trace we represent. */
+ if (!isListeningTo(sig.getTrace())) {
+ return;
+ }
+
+ final TmfStatisticsTree statsData = TmfStatisticsTreeManager.getStatTree(getTreeID());
+ Map<String, Long> map = sig.getEventsPerType();
+ String name = sig.getTrace().getName();
+ boolean isGlobal = sig.isGlobal();
+
+ /*
+ * "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);
+ }
+
+ // ------------------------------------------------------------------------
+ // Class methods
+ // ------------------------------------------------------------------------
+
/*
* Returns the primary control associated with this viewer.
*
/**
* Tells if the viewer is listening to a trace from the selected experiment.
*
- * @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;
* The range to request to the experiment
*/
protected void requestData(final TmfExperiment experiment, final TmfTimeRange timeRange) {
- final Thread thread = new Thread("Statistics view build") { //$NON-NLS-1$
- @Override
- public void run() {
- buildStatisticsTree(experiment, timeRange, true);
- }
- };
- thread.start();
+ buildStatisticsTree(experiment, timeRange, true);
}
/**
*/
protected void requestTimeRangeData(final TmfExperiment experiment, final TmfTimeRange timeRange) {
fRequestedTimerange = timeRange;
-
- final Thread thread = new Thread("Statistics view build") { //$NON-NLS-1$
- @Override
- public void run() {
- buildStatisticsTree(experiment, timeRange, false);
- }
- };
- thread.start();
+ buildStatisticsTree(experiment, timeRange, false);
}
/**
* listening to multiple traces.
*/
for (final ITmfTrace trace : experiment.getTraces()) {
- if (!isListeningTo(trace.getName())) {
+ if (!isListeningTo(trace)) {
continue;
}
final ITmfStatistics stats = trace.getStatistics();
if (stats == null) {
/*
- * The state system is not accessible yet for this trace.
- * Try the next one.
+ * The statistics provider for this trace is not accessible
+ * (yet?). Try the next one.
*/
continue;
}
- updateValues(statsData, trace, timeRange, isGlobal);
+ /* The generic statistics are stored in nanoseconds, so we must make
+ * sure the time range is scaled correctly. */
+ ITmfTimestamp start = timeRange.getStartTime().normalize(0, TIME_SCALE);
+ ITmfTimestamp end = timeRange.getEndTime().normalize(0, TIME_SCALE);
- modelComplete(isGlobal);
+ /*
+ * Send a request to update the statistics view. The result will
+ * be sent through a {@link TmfStatsUpdatedSignal}, and will be
+ * processed by the signal handler.
+ */
+ trace.getStatistics().updateStats(isGlobal, start, end);
}
}
}
- /**
- * Update the statistics values. It can be extended by subclasses if they
- * want to show something other than the base information in their viewer.
- * They can decide to show the base information too, by calling
- * super.updateValues() or not.
- *
- * @param statsData
- * The statistics tree we are updating
- * @param trace
- * The trace related to these statistics
- * @param timeRange
- * The time range for which we are updating. For updates to the
- * global data this should be the whole (available) time range of
- * the trace.
- * @param isGlobal
- * Are we updating the Global data (for the complete time range
- * of the trace), or the selected time range data?
- */
- protected void updateValues(TmfStatisticsTree statsData, ITmfTrace trace,
- TmfTimeRange timeRange, boolean isGlobal) {
- ITmfStatistics stats = trace.getStatistics();
-
- /*
- * "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)
- */
-
- /* The generic statistics are stored in nanoseconds, so we must make
- * sure the time range is scaled correctly. */
- ITmfTimestamp start = timeRange.getStartTime().normalize(0, TIME_SCALE);
- ITmfTimestamp end = timeRange.getEndTime().normalize(0, TIME_SCALE);
- String name = trace.getName();
-
- /*
- * Fill in the Total row (cell A or B, depending if isGlobal)
- * (we can still use .getEventsInRange(), even if it's global,
- * start and end will cover the whole trace)
- */
- long globalTotal = stats.getEventsInRange(start, end);
- statsData.setTotal(name, isGlobal, globalTotal);
-
- /* Fill in an the event counts (either cells C or D) */
- Map<String, Long> map = stats.getEventTypesInRange(start, end);
- for (Map.Entry<String, Long> entry : map.entrySet()) {
- statsData.setTypeCount(name, entry.getKey(), isGlobal, entry.getValue());
- }
- }
-
/**
* Resets the number of events within the time range
*/