1 /*******************************************************************************
2 * Copyright (c) 2012 Ericsson
4 * All rights reserved. This program and the accompanying materials are
5 * made available under the terms of the Eclipse Public License v1.0 which
6 * accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
10 * Alexandre Montplaisir - Initial API and implementation
11 ******************************************************************************/
13 package org
.eclipse
.linuxtools
.tmf
.core
.statistics
;
16 import java
.util
.HashMap
;
17 import java
.util
.List
;
20 import org
.eclipse
.core
.resources
.IResource
;
21 import org
.eclipse
.core
.runtime
.CoreException
;
22 import org
.eclipse
.linuxtools
.tmf
.core
.TmfCommonConstants
;
23 import org
.eclipse
.linuxtools
.tmf
.core
.event
.ITmfTimestamp
;
24 import org
.eclipse
.linuxtools
.tmf
.core
.exceptions
.AttributeNotFoundException
;
25 import org
.eclipse
.linuxtools
.tmf
.core
.exceptions
.StateSystemDisposedException
;
26 import org
.eclipse
.linuxtools
.tmf
.core
.exceptions
.StateValueTypeException
;
27 import org
.eclipse
.linuxtools
.tmf
.core
.exceptions
.TimeRangeException
;
28 import org
.eclipse
.linuxtools
.tmf
.core
.exceptions
.TmfTraceException
;
29 import org
.eclipse
.linuxtools
.tmf
.core
.interval
.ITmfStateInterval
;
30 import org
.eclipse
.linuxtools
.tmf
.core
.signal
.TmfSignal
;
31 import org
.eclipse
.linuxtools
.tmf
.core
.signal
.TmfSignalManager
;
32 import org
.eclipse
.linuxtools
.tmf
.core
.signal
.TmfStatsUpdatedSignal
;
33 import org
.eclipse
.linuxtools
.tmf
.core
.statesystem
.IStateChangeInput
;
34 import org
.eclipse
.linuxtools
.tmf
.core
.statesystem
.ITmfStateSystem
;
35 import org
.eclipse
.linuxtools
.tmf
.core
.statesystem
.StateSystemManager
;
36 import org
.eclipse
.linuxtools
.tmf
.core
.trace
.ITmfTrace
;
39 * Implementation of ITmfStatistics which uses a state history for storing its
42 * It requires building the history first, but gives very fast response times
43 * when built : Queries are O(log n) wrt the size of the trace, and O(1) wrt to
44 * the size of the time interval selected.
46 * @author Alexandre Montplaisir
50 public class TmfStateStatistics
implements ITmfStatistics
{
52 /** ID for the statistics state system */
53 public static final String STATE_ID
= "org.eclipse.linuxtools.tmf.statistics"; //$NON-NLS-1$
55 /** Filename the "statistics state history" file will have */
56 private static final String STATS_STATE_FILENAME
= "statistics.ht"; //$NON-NLS-1$
58 private final ITmfTrace trace
;
61 * The state system that's used to stored the statistics. It's hidden from
62 * the trace, so that it doesn't conflict with ITmfTrace.getStateSystem()
63 * (which is something else!)
65 private final ITmfStateSystem stats
;
68 * Empty constructor. The resulting TmfStatistics object will not be usable,
69 * but it might be needed for sub-classes.
71 public TmfStateStatistics() {
80 * The trace for which we build these statistics
81 * @throws TmfTraceException
82 * If something went wrong trying to initialize the statistics
84 public TmfStateStatistics(ITmfTrace trace
) throws TmfTraceException
{
85 /* Set up the path to the history tree file we'll use */
87 IResource resource
= trace
.getResource();
88 String supplDirectory
= null;
91 // get the directory where the history file will be stored.
92 supplDirectory
= resource
.getPersistentProperty(TmfCommonConstants
.TRACE_SUPPLEMENTARY_FOLDER
);
93 } catch (CoreException e
) {
94 throw new TmfTraceException(e
.toString(), e
);
97 final File htFile
= new File(supplDirectory
+ File
.separator
+ STATS_STATE_FILENAME
);
98 final IStateChangeInput htInput
= new StatsStateProvider(trace
);
100 this.stats
= StateSystemManager
.loadStateHistory(htFile
, htInput
, STATE_ID
, false);
104 * Manual constructor. This should be used if the trace's Resource is null
105 * (ie, for unit tests). It requires specifying the location of the history
109 * The trace for which we build these statistics
111 * The location of the state history file to build for the stats
112 * @throws TmfTraceException
113 * If the file could not be written to
115 public TmfStateStatistics(ITmfTrace trace
, File historyFile
) throws TmfTraceException
{
117 final IStateChangeInput htInput
= new StatsStateProvider(trace
);
118 this.stats
= StateSystemManager
.loadStateHistory(historyFile
, htInput
, STATE_ID
, true);
121 // ------------------------------------------------------------------------
123 // ------------------------------------------------------------------------
126 public void dispose() {
131 public void updateStats(final boolean isGlobal
, final ITmfTimestamp start
,
132 final ITmfTimestamp end
) {
134 * Since we are currently in a signal handler (ie, in the UI thread),
135 * and since state system queries can be arbitrarily long (O(log n) wrt
136 * the size of the trace), we will run those queries in a separate
137 * thread and update the statistics view out-of-band.
139 Thread statsThread
= new Thread("Statistics update") { //$NON-NLS-1$
143 Map
<String
, Long
> map
;
145 /* Wait until the history building completed */
146 if (!stats
.waitUntilBuilt()) {
150 /* Range should be valid for both global and time range queries */
151 total
= getEventsInRange(start
, end
);
152 map
= getEventTypesInRange(start
, end
);
154 /* Send the signal to notify the stats viewer to update its display */
155 TmfSignal sig
= new TmfStatsUpdatedSignal(this, trace
, isGlobal
, total
, map
);
156 TmfSignalManager
.dispatchSignal(sig
);
165 public long getEventsTotal() {
166 /* We need the complete state history to be built to answer this. */
167 stats
.waitUntilBuilt();
169 long endTime
= stats
.getCurrentEndTime();
173 final int quark
= stats
.getQuarkAbsolute(Attributes
.TOTAL
);
174 count
= stats
.querySingleState(endTime
, quark
).getStateValue().unboxInt();
176 } catch (TimeRangeException e
) {
177 /* Assume there is no events for that range */
179 } catch (AttributeNotFoundException e
) {
181 } catch (StateValueTypeException e
) {
183 } catch (StateSystemDisposedException e
) {
191 public Map
<String
, Long
> getEventTypesTotal() {
192 /* We need the complete state history to be built to answer this. */
193 stats
.waitUntilBuilt();
195 Map
<String
, Long
> map
= new HashMap
<String
, Long
>();
196 long endTime
= stats
.getCurrentEndTime();
199 /* Get the list of quarks, one for each even type in the database */
200 int quark
= stats
.getQuarkAbsolute(Attributes
.EVENT_TYPES
);
201 List
<Integer
> quarks
= stats
.getSubAttributes(quark
, false);
203 /* Since we want the total we can look only at the end */
204 List
<ITmfStateInterval
> endState
= stats
.queryFullState(endTime
);
208 for (int typeQuark
: quarks
) {
209 curEventName
= stats
.getAttributeName(typeQuark
);
210 eventCount
= endState
.get(typeQuark
).getStateValue().unboxInt();
211 map
.put(curEventName
, eventCount
);
214 } catch (TimeRangeException e
) {
215 /* Assume there is no events, nothing will be put in the map. */
216 } catch (AttributeNotFoundException e
) {
218 } catch (StateValueTypeException e
) {
220 } catch (StateSystemDisposedException e
) {
227 public long getEventsInRange(ITmfTimestamp start
, ITmfTimestamp end
) {
228 // FIXME Instead of waiting until the end, we could check the current
229 // end time, and answer as soon as possible...
230 stats
.waitUntilBuilt();
232 int countAtStart
= 0, countAtEnd
= 0;
233 long startTime
= checkStartTime(start
);
234 long endTime
= checkEndTime(end
);
237 final int quark
= stats
.getQuarkAbsolute(Attributes
.TOTAL
);
238 if (startTime
== stats
.getStartTime()) {
241 /* State system works that way... */
242 countAtStart
= stats
.querySingleState(startTime
- 1, quark
).getStateValue().unboxInt();
244 countAtEnd
= stats
.querySingleState(endTime
, quark
).getStateValue().unboxInt();
246 } catch (TimeRangeException e
) {
247 /* Assume there is no events for that range */
249 } catch (AttributeNotFoundException e
) {
251 } catch (StateValueTypeException e
) {
253 } catch (StateSystemDisposedException e
) {
257 long total
= countAtEnd
- countAtStart
;
262 public Map
<String
, Long
> getEventTypesInRange(ITmfTimestamp start
, ITmfTimestamp end
) {
263 // FIXME Instead of waiting until the end, we could check the current
264 // end time, and answer as soon as possible...
265 stats
.waitUntilBuilt();
267 Map
<String
, Long
> map
= new HashMap
<String
, Long
>();
269 /* Make sure the start/end times are within the state history, so we
270 * don't get TimeRange exceptions.
272 long startTime
= checkStartTime(start
);
273 long endTime
= checkEndTime(end
);
276 /* Get the list of quarks, one for each even type in the database */
277 int quark
= stats
.getQuarkAbsolute(Attributes
.EVENT_TYPES
);
278 List
<Integer
> quarks
= stats
.getSubAttributes(quark
, false);
280 List
<ITmfStateInterval
> endState
= stats
.queryFullState(endTime
);
283 long countAtStart
, countAtEnd
, eventCount
;
285 if (startTime
== stats
.getStartTime()) {
286 /* Only use the values picked up at the end time */
287 for (int typeQuark
: quarks
) {
288 curEventName
= stats
.getAttributeName(typeQuark
);
289 eventCount
= endState
.get(typeQuark
).getStateValue().unboxInt();
290 if (eventCount
== -1) {
293 map
.put(curEventName
, eventCount
);
297 * Query the start time at -1, so the beginning of the interval
300 List
<ITmfStateInterval
> startState
= stats
.queryFullState(startTime
- 1);
301 for (int typeQuark
: quarks
) {
302 curEventName
= stats
.getAttributeName(typeQuark
);
303 countAtStart
= startState
.get(typeQuark
).getStateValue().unboxInt();
304 countAtEnd
= endState
.get(typeQuark
).getStateValue().unboxInt();
306 if (countAtStart
== -1) {
309 if (countAtEnd
== -1) {
312 eventCount
= countAtEnd
- countAtStart
;
313 map
.put(curEventName
, eventCount
);
317 } catch (TimeRangeException e
) {
318 /* Assume there is no events, nothing will be put in the map. */
319 } catch (AttributeNotFoundException e
) {
321 * These other exception types would show a logic problem however,
322 * so they should not happen.
325 } catch (StateValueTypeException e
) {
327 } catch (StateSystemDisposedException e
) {
333 protected long checkStartTime(ITmfTimestamp startTs
) {
334 long start
= startTs
.normalize(0, ITmfTimestamp
.NANOSECOND_SCALE
).getValue();
335 if (start
< stats
.getStartTime()) {
336 return stats
.getStartTime();
341 protected long checkEndTime(ITmfTimestamp endTs
) {
342 long end
= endTs
.normalize(0, ITmfTimestamp
.NANOSECOND_SCALE
).getValue();
343 if (end
> stats
.getCurrentEndTime()) {
344 return stats
.getCurrentEndTime();
351 * The attribute names that are used in the state provider
353 public static class Attributes
{
355 /** Total nb of events */
356 public static final String TOTAL
= "total"; //$NON-NLS-1$
359 public static final String EVENT_TYPES
= "event_types"; //$NON-NLS-1$<