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
.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);
103 * Manual constructor. This should be used if the trace's Resource is null
104 * (ie, for unit tests). It requires specifying the location of the history
108 * The trace for which we build these statistics
110 * The location of the state history file to build for the stats
111 * @throws TmfTraceException
112 * If the file could not be written to
114 public TmfStateStatistics(ITmfTrace trace
, File historyFile
) throws TmfTraceException
{
116 final IStateChangeInput htInput
= new StatsStateProvider(trace
);
117 this.stats
= StateSystemManager
.loadStateHistory(historyFile
, htInput
, true);
120 // ------------------------------------------------------------------------
122 // ------------------------------------------------------------------------
125 public void dispose() {
130 public void updateStats(final boolean isGlobal
, final long start
,
133 * Since we are currently in a signal handler (ie, in the UI thread),
134 * and since state system queries can be arbitrarily long (O(log n) wrt
135 * the size of the trace), we will run those queries in a separate
136 * thread and update the statistics view out-of-band.
138 Thread statsThread
= new Thread("Statistics update") { //$NON-NLS-1$
142 Map
<String
, Long
> map
;
144 /* Wait until the history building completed */
145 if (!stats
.waitUntilBuilt()) {
149 /* Range should be valid for both global and time range queries */
150 total
= getEventsInRange(start
, end
);
151 map
= getEventTypesInRange(start
, end
);
153 /* Send the signal to notify the stats viewer to update its display */
154 TmfSignal sig
= new TmfStatsUpdatedSignal(this, trace
, isGlobal
, total
, map
);
155 TmfSignalManager
.dispatchSignal(sig
);
163 public List
<Long
> histogramQuery(final long start
, final long end
, final int nb
) {
164 final List
<Long
> list
= new LinkedList
<Long
>();
165 final long increment
= (end
- start
) / nb
;
167 /* Wait until the history building completed */
168 if (!stats
.waitUntilBuilt()) {
173 * We will do one state system query per "border", and save the
174 * differences between each border.
176 long prevTotal
= (start
== stats
.getStartTime()) ?
0 : getEventCountAt(start
);
177 long curTime
= start
+ increment
;
179 long curTotal
, count
;
180 for (int i
= 0; i
< nb
- 1; i
++) {
181 curTotal
= getEventCountAt(curTime
);
182 count
= curTotal
- prevTotal
;
185 curTime
+= increment
;
186 prevTotal
= curTotal
;
190 * For the last bucket, we'll stretch its end time to the end time of
191 * the requested range, in case it got truncated down.
193 curTotal
= getEventCountAt(end
);
194 count
= curTotal
- prevTotal
;
201 public long getEventsTotal() {
202 /* We need the complete state history to be built to answer this. */
203 stats
.waitUntilBuilt();
205 long endTime
= stats
.getCurrentEndTime();
209 final int quark
= stats
.getQuarkAbsolute(Attributes
.TOTAL
);
210 count
= stats
.querySingleState(endTime
, quark
).getStateValue().unboxInt();
212 } catch (TimeRangeException e
) {
213 /* Assume there is no events for that range */
215 } catch (AttributeNotFoundException e
) {
217 } catch (StateValueTypeException e
) {
219 } catch (StateSystemDisposedException e
) {
227 public Map
<String
, Long
> getEventTypesTotal() {
228 /* We need the complete state history to be built to answer this. */
229 stats
.waitUntilBuilt();
231 Map
<String
, Long
> map
= new HashMap
<String
, Long
>();
232 long endTime
= stats
.getCurrentEndTime();
235 /* Get the list of quarks, one for each even type in the database */
236 int quark
= stats
.getQuarkAbsolute(Attributes
.EVENT_TYPES
);
237 List
<Integer
> quarks
= stats
.getSubAttributes(quark
, false);
239 /* Since we want the total we can look only at the end */
240 List
<ITmfStateInterval
> endState
= stats
.queryFullState(endTime
);
244 for (int typeQuark
: quarks
) {
245 curEventName
= stats
.getAttributeName(typeQuark
);
246 eventCount
= endState
.get(typeQuark
).getStateValue().unboxInt();
247 map
.put(curEventName
, eventCount
);
250 } catch (TimeRangeException e
) {
251 /* Assume there is no events, nothing will be put in the map. */
252 } catch (AttributeNotFoundException e
) {
254 } catch (StateValueTypeException e
) {
256 } catch (StateSystemDisposedException e
) {
263 public long getEventsInRange(long start
, long end
) {
264 // FIXME Instead of waiting until the end, we could check the current
265 // end time, and answer as soon as possible...
266 stats
.waitUntilBuilt();
269 if (start
== stats
.getStartTime()) {
273 * We want the events happening at "start" to be included, so we'll
274 * need to query one unit before that point.
276 startCount
= getEventCountAt(start
- 1);
278 long endCount
= getEventCountAt(end
);
280 return endCount
- startCount
;
284 public Map
<String
, Long
> getEventTypesInRange(long start
, long end
) {
285 // FIXME Instead of waiting until the end, we could check the current
286 // end time, and answer as soon as possible...
287 stats
.waitUntilBuilt();
289 Map
<String
, Long
> map
= new HashMap
<String
, Long
>();
291 /* Make sure the start/end times are within the state history, so we
292 * don't get TimeRange exceptions.
294 long startTime
= checkStartTime(start
);
295 long endTime
= checkEndTime(end
);
298 /* Get the list of quarks, one for each even type in the database */
299 int quark
= stats
.getQuarkAbsolute(Attributes
.EVENT_TYPES
);
300 List
<Integer
> quarks
= stats
.getSubAttributes(quark
, false);
302 List
<ITmfStateInterval
> endState
= stats
.queryFullState(endTime
);
305 long countAtStart
, countAtEnd
, eventCount
;
307 if (startTime
== stats
.getStartTime()) {
308 /* Only use the values picked up at the end time */
309 for (int typeQuark
: quarks
) {
310 curEventName
= stats
.getAttributeName(typeQuark
);
311 eventCount
= endState
.get(typeQuark
).getStateValue().unboxInt();
312 if (eventCount
== -1) {
315 map
.put(curEventName
, eventCount
);
319 * Query the start time at -1, so the beginning of the interval
322 List
<ITmfStateInterval
> startState
= stats
.queryFullState(startTime
- 1);
323 for (int typeQuark
: quarks
) {
324 curEventName
= stats
.getAttributeName(typeQuark
);
325 countAtStart
= startState
.get(typeQuark
).getStateValue().unboxInt();
326 countAtEnd
= endState
.get(typeQuark
).getStateValue().unboxInt();
328 if (countAtStart
== -1) {
331 if (countAtEnd
== -1) {
334 eventCount
= countAtEnd
- countAtStart
;
335 map
.put(curEventName
, eventCount
);
339 } catch (TimeRangeException e
) {
340 /* Assume there is no events, nothing will be put in the map. */
341 } catch (AttributeNotFoundException e
) {
343 * These other exception types would show a logic problem however,
344 * so they should not happen.
347 } catch (StateValueTypeException e
) {
349 } catch (StateSystemDisposedException e
) {
355 private long getEventCountAt(long timestamp
) {
356 /* Make sure the target time is within the range of the history */
357 long ts
= checkStartTime(timestamp
);
358 ts
= checkEndTime(ts
);
361 final int quark
= stats
.getQuarkAbsolute(Attributes
.TOTAL
);
362 long count
= stats
.querySingleState(ts
, quark
).getStateValue().unboxInt();
365 } catch (TimeRangeException e
) {
366 /* Assume there is no events for that range */
367 } catch (AttributeNotFoundException e
) {
369 } catch (StateValueTypeException e
) {
371 } catch (StateSystemDisposedException e
) {
378 private long checkStartTime(long initialStart
) {
379 long start
= initialStart
;
380 if (start
< stats
.getStartTime()) {
381 return stats
.getStartTime();
386 private long checkEndTime(long initialEnd
) {
387 long end
= initialEnd
;
388 if (end
> stats
.getCurrentEndTime()) {
389 return stats
.getCurrentEndTime();
396 * The attribute names that are used in the state provider
398 public static class Attributes
{
400 /** Total nb of events */
401 public static final String TOTAL
= "total"; //$NON-NLS-1$
404 public static final String EVENT_TYPES
= "event_types"; //$NON-NLS-1$<