1 /*******************************************************************************
2 * Copyright (c) 2012, 2013 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
.LinkedList
;
18 import java
.util
.List
;
21 import org
.eclipse
.linuxtools
.tmf
.core
.exceptions
.AttributeNotFoundException
;
22 import org
.eclipse
.linuxtools
.tmf
.core
.exceptions
.StateSystemDisposedException
;
23 import org
.eclipse
.linuxtools
.tmf
.core
.exceptions
.StateValueTypeException
;
24 import org
.eclipse
.linuxtools
.tmf
.core
.exceptions
.TimeRangeException
;
25 import org
.eclipse
.linuxtools
.tmf
.core
.exceptions
.TmfTraceException
;
26 import org
.eclipse
.linuxtools
.tmf
.core
.interval
.ITmfStateInterval
;
27 import org
.eclipse
.linuxtools
.tmf
.core
.signal
.TmfSignal
;
28 import org
.eclipse
.linuxtools
.tmf
.core
.signal
.TmfSignalManager
;
29 import org
.eclipse
.linuxtools
.tmf
.core
.signal
.TmfStatsUpdatedSignal
;
30 import org
.eclipse
.linuxtools
.tmf
.core
.statesystem
.ITmfStateProvider
;
31 import org
.eclipse
.linuxtools
.tmf
.core
.statesystem
.ITmfStateSystem
;
32 import org
.eclipse
.linuxtools
.tmf
.core
.statesystem
.TmfStateSystemFactory
;
33 import org
.eclipse
.linuxtools
.tmf
.core
.trace
.ITmfTrace
;
34 import org
.eclipse
.linuxtools
.tmf
.core
.trace
.TmfTraceManager
;
37 * Implementation of ITmfStatistics which uses a state history for storing its
40 * It requires building the history first, but gives very fast response times
41 * when built : Queries are O(log n) wrt the size of the trace, and O(1) wrt to
42 * the size of the time interval selected.
44 * @author Alexandre Montplaisir
47 public class TmfStateStatistics
implements ITmfStatistics
{
49 /** ID for the statistics state system */
50 public static final String STATE_ID
= "org.eclipse.linuxtools.tmf.statistics"; //$NON-NLS-1$
52 /** Filename the "statistics state history" file will have */
53 private static final String STATS_STATE_FILENAME
= "statistics.ht"; //$NON-NLS-1$
55 private final ITmfTrace trace
;
58 * The state system that's used to stored the statistics. It's hidden from
59 * the trace, so that it doesn't conflict with ITmfTrace.getStateSystem()
60 * (which is something else!)
62 private final ITmfStateSystem stats
;
65 * Empty constructor. The resulting TmfStatistics object will not be usable,
66 * but it might be needed for sub-classes.
68 public TmfStateStatistics() {
77 * The trace for which we build these statistics
78 * @throws TmfTraceException
79 * If something went wrong trying to initialize the statistics
81 public TmfStateStatistics(ITmfTrace trace
) throws TmfTraceException
{
84 String directory
= TmfTraceManager
.getSupplementaryFileDir(trace
);
85 final File htFile
= new File(directory
+ STATS_STATE_FILENAME
);
86 final ITmfStateProvider htInput
= new StatsStateProvider(trace
);
88 this.stats
= TmfStateSystemFactory
.newFullHistory(htFile
, htInput
, false);
89 registerStateSystems();
93 * Manual constructor. This should be used if the trace's Resource is null
94 * (ie, for unit tests). It requires specifying the location of the history
98 * The trace for which we build these statistics
100 * The location of the state history file to build for the stats
101 * @throws TmfTraceException
102 * If the file could not be written to
104 public TmfStateStatistics(ITmfTrace trace
, File historyFile
) throws TmfTraceException
{
106 final ITmfStateProvider htInput
= new StatsStateProvider(trace
);
107 this.stats
= TmfStateSystemFactory
.newFullHistory(historyFile
, htInput
, true);
108 registerStateSystems();
112 * Register the state systems used here into the trace's state system array.
114 private void registerStateSystems() {
115 trace
.registerStateSystem(STATE_ID
, stats
);
118 // ------------------------------------------------------------------------
120 // ------------------------------------------------------------------------
123 public void dispose() {
128 public void updateStats(final boolean isGlobal
, final long start
,
131 * Since we are currently in a signal handler (ie, in the UI thread),
132 * and since state system queries can be arbitrarily long (O(log n) wrt
133 * the size of the trace), we will run those queries in a separate
134 * thread and update the statistics view out-of-band.
136 Thread statsThread
= new Thread("Statistics update") { //$NON-NLS-1$
140 Map
<String
, Long
> map
;
142 /* Wait until the history building completed */
143 if (!stats
.waitUntilBuilt()) {
147 /* Range should be valid for both global and time range queries */
148 total
= getEventsInRange(start
, end
);
149 map
= getEventTypesInRange(start
, end
);
151 /* Send the signal to notify the stats viewer to update its display */
152 TmfSignal sig
= new TmfStatsUpdatedSignal(this, trace
, isGlobal
, total
, map
);
153 TmfSignalManager
.dispatchSignal(sig
);
161 public List
<Long
> histogramQuery(final long start
, final long end
, final int nb
) {
162 final List
<Long
> list
= new LinkedList
<Long
>();
163 final long increment
= (end
- start
) / nb
;
165 /* Wait until the history building completed */
166 if (!stats
.waitUntilBuilt()) {
171 * We will do one state system query per "border", and save the
172 * differences between each border.
174 long prevTotal
= (start
== stats
.getStartTime()) ?
0 : getEventCountAt(start
);
175 long curTime
= start
+ increment
;
177 long curTotal
, count
;
178 for (int i
= 0; i
< nb
- 1; i
++) {
179 curTotal
= getEventCountAt(curTime
);
180 count
= curTotal
- prevTotal
;
183 curTime
+= increment
;
184 prevTotal
= curTotal
;
188 * For the last bucket, we'll stretch its end time to the end time of
189 * the requested range, in case it got truncated down.
191 curTotal
= getEventCountAt(end
);
192 count
= curTotal
- prevTotal
;
199 public long getEventsTotal() {
200 /* We need the complete state history to be built to answer this. */
201 stats
.waitUntilBuilt();
203 long endTime
= stats
.getCurrentEndTime();
207 final int quark
= stats
.getQuarkAbsolute(Attributes
.TOTAL
);
208 count
= stats
.querySingleState(endTime
, quark
).getStateValue().unboxInt();
210 } catch (TimeRangeException e
) {
211 /* Assume there is no events for that range */
213 } catch (AttributeNotFoundException e
) {
215 } catch (StateValueTypeException e
) {
217 } catch (StateSystemDisposedException e
) {
225 public Map
<String
, Long
> getEventTypesTotal() {
226 /* We need the complete state history to be built to answer this. */
227 stats
.waitUntilBuilt();
229 Map
<String
, Long
> map
= new HashMap
<String
, Long
>();
230 long endTime
= stats
.getCurrentEndTime();
233 /* Get the list of quarks, one for each even type in the database */
234 int quark
= stats
.getQuarkAbsolute(Attributes
.EVENT_TYPES
);
235 List
<Integer
> quarks
= stats
.getSubAttributes(quark
, false);
237 /* Since we want the total we can look only at the end */
238 List
<ITmfStateInterval
> endState
= stats
.queryFullState(endTime
);
242 for (int typeQuark
: quarks
) {
243 curEventName
= stats
.getAttributeName(typeQuark
);
244 eventCount
= endState
.get(typeQuark
).getStateValue().unboxInt();
245 map
.put(curEventName
, eventCount
);
248 } catch (TimeRangeException e
) {
249 /* Assume there is no events, nothing will be put in the map. */
250 } catch (AttributeNotFoundException e
) {
252 } catch (StateValueTypeException e
) {
254 } catch (StateSystemDisposedException e
) {
261 public long getEventsInRange(long start
, long end
) {
262 // FIXME Instead of waiting until the end, we could check the current
263 // end time, and answer as soon as possible...
264 stats
.waitUntilBuilt();
267 if (start
== stats
.getStartTime()) {
271 * We want the events happening at "start" to be included, so we'll
272 * need to query one unit before that point.
274 startCount
= getEventCountAt(start
- 1);
276 long endCount
= getEventCountAt(end
);
278 return endCount
- startCount
;
282 public Map
<String
, Long
> getEventTypesInRange(long start
, long end
) {
283 // FIXME Instead of waiting until the end, we could check the current
284 // end time, and answer as soon as possible...
285 stats
.waitUntilBuilt();
287 Map
<String
, Long
> map
= new HashMap
<String
, Long
>();
289 /* Make sure the start/end times are within the state history, so we
290 * don't get TimeRange exceptions.
292 long startTime
= checkStartTime(start
);
293 long endTime
= checkEndTime(end
);
296 /* Get the list of quarks, one for each even type in the database */
297 int quark
= stats
.getQuarkAbsolute(Attributes
.EVENT_TYPES
);
298 List
<Integer
> quarks
= stats
.getSubAttributes(quark
, false);
300 List
<ITmfStateInterval
> endState
= stats
.queryFullState(endTime
);
303 long countAtStart
, countAtEnd
, eventCount
;
305 if (startTime
== stats
.getStartTime()) {
306 /* Only use the values picked up at the end time */
307 for (int typeQuark
: quarks
) {
308 curEventName
= stats
.getAttributeName(typeQuark
);
309 eventCount
= endState
.get(typeQuark
).getStateValue().unboxInt();
310 if (eventCount
== -1) {
313 map
.put(curEventName
, eventCount
);
317 * Query the start time at -1, so the beginning of the interval
320 List
<ITmfStateInterval
> startState
= stats
.queryFullState(startTime
- 1);
321 for (int typeQuark
: quarks
) {
322 curEventName
= stats
.getAttributeName(typeQuark
);
323 countAtStart
= startState
.get(typeQuark
).getStateValue().unboxInt();
324 countAtEnd
= endState
.get(typeQuark
).getStateValue().unboxInt();
326 if (countAtStart
== -1) {
329 if (countAtEnd
== -1) {
332 eventCount
= countAtEnd
- countAtStart
;
333 map
.put(curEventName
, eventCount
);
337 } catch (TimeRangeException e
) {
338 /* Assume there is no events, nothing will be put in the map. */
339 } catch (AttributeNotFoundException e
) {
341 * These other exception types would show a logic problem however,
342 * so they should not happen.
345 } catch (StateValueTypeException e
) {
347 } catch (StateSystemDisposedException e
) {
353 private long getEventCountAt(long timestamp
) {
354 /* Make sure the target time is within the range of the history */
355 long ts
= checkStartTime(timestamp
);
356 ts
= checkEndTime(ts
);
359 final int quark
= stats
.getQuarkAbsolute(Attributes
.TOTAL
);
360 long count
= stats
.querySingleState(ts
, quark
).getStateValue().unboxInt();
363 } catch (TimeRangeException e
) {
364 /* Assume there is no events for that range */
365 } catch (AttributeNotFoundException e
) {
367 } catch (StateValueTypeException e
) {
369 } catch (StateSystemDisposedException e
) {
376 private long checkStartTime(long initialStart
) {
377 long start
= initialStart
;
378 if (start
< stats
.getStartTime()) {
379 return stats
.getStartTime();
384 private long checkEndTime(long initialEnd
) {
385 long end
= initialEnd
;
386 if (end
> stats
.getCurrentEndTime()) {
387 return stats
.getCurrentEndTime();
394 * The attribute names that are used in the state provider
396 public static class Attributes
{
398 /** Total nb of events */
399 public static final String TOTAL
= "total"; //$NON-NLS-1$
402 public static final String EVENT_TYPES
= "event_types"; //$NON-NLS-1$<