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
.core
.resources
.IResource
;
22 import org
.eclipse
.core
.runtime
.CoreException
;
23 import org
.eclipse
.linuxtools
.tmf
.core
.TmfCommonConstants
;
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
49 public class TmfStateStatistics
implements ITmfStatistics
{
51 /** ID for the statistics state system */
52 public static final String STATE_ID
= "org.eclipse.linuxtools.tmf.statistics"; //$NON-NLS-1$
54 /** Filename the "statistics state history" file will have */
55 private static final String STATS_STATE_FILENAME
= "statistics.ht"; //$NON-NLS-1$
57 private final ITmfTrace trace
;
60 * The state system that's used to stored the statistics. It's hidden from
61 * the trace, so that it doesn't conflict with ITmfTrace.getStateSystem()
62 * (which is something else!)
64 private final ITmfStateSystem stats
;
67 * Empty constructor. The resulting TmfStatistics object will not be usable,
68 * but it might be needed for sub-classes.
70 public TmfStateStatistics() {
79 * The trace for which we build these statistics
80 * @throws TmfTraceException
81 * If something went wrong trying to initialize the statistics
83 public TmfStateStatistics(ITmfTrace trace
) throws TmfTraceException
{
84 /* Set up the path to the history tree file we'll use */
86 IResource resource
= trace
.getResource();
87 String supplDirectory
= null;
90 // get the directory where the history file will be stored.
91 supplDirectory
= resource
.getPersistentProperty(TmfCommonConstants
.TRACE_SUPPLEMENTARY_FOLDER
);
92 } catch (CoreException e
) {
93 throw new TmfTraceException(e
.toString(), e
);
96 final File htFile
= new File(supplDirectory
+ File
.separator
+ STATS_STATE_FILENAME
);
97 final IStateChangeInput htInput
= new StatsStateProvider(trace
);
99 this.stats
= StateSystemManager
.loadStateHistory(htFile
, htInput
, false);
100 registerStateSystems();
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
, true);
119 registerStateSystems();
123 * Register the state systems used here into the trace's state system array.
125 private void registerStateSystems() {
126 trace
.registerStateSystem(STATE_ID
, stats
);
129 // ------------------------------------------------------------------------
131 // ------------------------------------------------------------------------
134 public void dispose() {
139 public void updateStats(final boolean isGlobal
, final long start
,
142 * Since we are currently in a signal handler (ie, in the UI thread),
143 * and since state system queries can be arbitrarily long (O(log n) wrt
144 * the size of the trace), we will run those queries in a separate
145 * thread and update the statistics view out-of-band.
147 Thread statsThread
= new Thread("Statistics update") { //$NON-NLS-1$
151 Map
<String
, Long
> map
;
153 /* Wait until the history building completed */
154 if (!stats
.waitUntilBuilt()) {
158 /* Range should be valid for both global and time range queries */
159 total
= getEventsInRange(start
, end
);
160 map
= getEventTypesInRange(start
, end
);
162 /* Send the signal to notify the stats viewer to update its display */
163 TmfSignal sig
= new TmfStatsUpdatedSignal(this, trace
, isGlobal
, total
, map
);
164 TmfSignalManager
.dispatchSignal(sig
);
172 public List
<Long
> histogramQuery(final long start
, final long end
, final int nb
) {
173 final List
<Long
> list
= new LinkedList
<Long
>();
174 final long increment
= (end
- start
) / nb
;
176 /* Wait until the history building completed */
177 if (!stats
.waitUntilBuilt()) {
182 * We will do one state system query per "border", and save the
183 * differences between each border.
185 long prevTotal
= (start
== stats
.getStartTime()) ?
0 : getEventCountAt(start
);
186 long curTime
= start
+ increment
;
188 long curTotal
, count
;
189 for (int i
= 0; i
< nb
- 1; i
++) {
190 curTotal
= getEventCountAt(curTime
);
191 count
= curTotal
- prevTotal
;
194 curTime
+= increment
;
195 prevTotal
= curTotal
;
199 * For the last bucket, we'll stretch its end time to the end time of
200 * the requested range, in case it got truncated down.
202 curTotal
= getEventCountAt(end
);
203 count
= curTotal
- prevTotal
;
210 public long getEventsTotal() {
211 /* We need the complete state history to be built to answer this. */
212 stats
.waitUntilBuilt();
214 long endTime
= stats
.getCurrentEndTime();
218 final int quark
= stats
.getQuarkAbsolute(Attributes
.TOTAL
);
219 count
= stats
.querySingleState(endTime
, quark
).getStateValue().unboxInt();
221 } catch (TimeRangeException e
) {
222 /* Assume there is no events for that range */
224 } catch (AttributeNotFoundException e
) {
226 } catch (StateValueTypeException e
) {
228 } catch (StateSystemDisposedException e
) {
236 public Map
<String
, Long
> getEventTypesTotal() {
237 /* We need the complete state history to be built to answer this. */
238 stats
.waitUntilBuilt();
240 Map
<String
, Long
> map
= new HashMap
<String
, Long
>();
241 long endTime
= stats
.getCurrentEndTime();
244 /* Get the list of quarks, one for each even type in the database */
245 int quark
= stats
.getQuarkAbsolute(Attributes
.EVENT_TYPES
);
246 List
<Integer
> quarks
= stats
.getSubAttributes(quark
, false);
248 /* Since we want the total we can look only at the end */
249 List
<ITmfStateInterval
> endState
= stats
.queryFullState(endTime
);
253 for (int typeQuark
: quarks
) {
254 curEventName
= stats
.getAttributeName(typeQuark
);
255 eventCount
= endState
.get(typeQuark
).getStateValue().unboxInt();
256 map
.put(curEventName
, eventCount
);
259 } catch (TimeRangeException e
) {
260 /* Assume there is no events, nothing will be put in the map. */
261 } catch (AttributeNotFoundException e
) {
263 } catch (StateValueTypeException e
) {
265 } catch (StateSystemDisposedException e
) {
272 public long getEventsInRange(long start
, long end
) {
273 // FIXME Instead of waiting until the end, we could check the current
274 // end time, and answer as soon as possible...
275 stats
.waitUntilBuilt();
278 if (start
== stats
.getStartTime()) {
282 * We want the events happening at "start" to be included, so we'll
283 * need to query one unit before that point.
285 startCount
= getEventCountAt(start
- 1);
287 long endCount
= getEventCountAt(end
);
289 return endCount
- startCount
;
293 public Map
<String
, Long
> getEventTypesInRange(long start
, long end
) {
294 // FIXME Instead of waiting until the end, we could check the current
295 // end time, and answer as soon as possible...
296 stats
.waitUntilBuilt();
298 Map
<String
, Long
> map
= new HashMap
<String
, Long
>();
300 /* Make sure the start/end times are within the state history, so we
301 * don't get TimeRange exceptions.
303 long startTime
= checkStartTime(start
);
304 long endTime
= checkEndTime(end
);
307 /* Get the list of quarks, one for each even type in the database */
308 int quark
= stats
.getQuarkAbsolute(Attributes
.EVENT_TYPES
);
309 List
<Integer
> quarks
= stats
.getSubAttributes(quark
, false);
311 List
<ITmfStateInterval
> endState
= stats
.queryFullState(endTime
);
314 long countAtStart
, countAtEnd
, eventCount
;
316 if (startTime
== stats
.getStartTime()) {
317 /* Only use the values picked up at the end time */
318 for (int typeQuark
: quarks
) {
319 curEventName
= stats
.getAttributeName(typeQuark
);
320 eventCount
= endState
.get(typeQuark
).getStateValue().unboxInt();
321 if (eventCount
== -1) {
324 map
.put(curEventName
, eventCount
);
328 * Query the start time at -1, so the beginning of the interval
331 List
<ITmfStateInterval
> startState
= stats
.queryFullState(startTime
- 1);
332 for (int typeQuark
: quarks
) {
333 curEventName
= stats
.getAttributeName(typeQuark
);
334 countAtStart
= startState
.get(typeQuark
).getStateValue().unboxInt();
335 countAtEnd
= endState
.get(typeQuark
).getStateValue().unboxInt();
337 if (countAtStart
== -1) {
340 if (countAtEnd
== -1) {
343 eventCount
= countAtEnd
- countAtStart
;
344 map
.put(curEventName
, eventCount
);
348 } catch (TimeRangeException e
) {
349 /* Assume there is no events, nothing will be put in the map. */
350 } catch (AttributeNotFoundException e
) {
352 * These other exception types would show a logic problem however,
353 * so they should not happen.
356 } catch (StateValueTypeException e
) {
358 } catch (StateSystemDisposedException e
) {
364 private long getEventCountAt(long timestamp
) {
365 /* Make sure the target time is within the range of the history */
366 long ts
= checkStartTime(timestamp
);
367 ts
= checkEndTime(ts
);
370 final int quark
= stats
.getQuarkAbsolute(Attributes
.TOTAL
);
371 long count
= stats
.querySingleState(ts
, quark
).getStateValue().unboxInt();
374 } catch (TimeRangeException e
) {
375 /* Assume there is no events for that range */
376 } catch (AttributeNotFoundException e
) {
378 } catch (StateValueTypeException e
) {
380 } catch (StateSystemDisposedException e
) {
387 private long checkStartTime(long initialStart
) {
388 long start
= initialStart
;
389 if (start
< stats
.getStartTime()) {
390 return stats
.getStartTime();
395 private long checkEndTime(long initialEnd
) {
396 long end
= initialEnd
;
397 if (end
> stats
.getCurrentEndTime()) {
398 return stats
.getCurrentEndTime();
405 * The attribute names that are used in the state provider
407 public static class Attributes
{
409 /** Total nb of events */
410 public static final String TOTAL
= "total"; //$NON-NLS-1$
413 public static final String EVENT_TYPES
= "event_types"; //$NON-NLS-1$<