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
.jdt
.annotation
.NonNull
;
22 import org
.eclipse
.linuxtools
.tmf
.core
.exceptions
.AttributeNotFoundException
;
23 import org
.eclipse
.linuxtools
.tmf
.core
.exceptions
.StateSystemDisposedException
;
24 import org
.eclipse
.linuxtools
.tmf
.core
.exceptions
.StateValueTypeException
;
25 import org
.eclipse
.linuxtools
.tmf
.core
.exceptions
.TimeRangeException
;
26 import org
.eclipse
.linuxtools
.tmf
.core
.exceptions
.TmfTraceException
;
27 import org
.eclipse
.linuxtools
.tmf
.core
.interval
.ITmfStateInterval
;
28 import org
.eclipse
.linuxtools
.tmf
.core
.signal
.TmfSignal
;
29 import org
.eclipse
.linuxtools
.tmf
.core
.signal
.TmfSignalManager
;
30 import org
.eclipse
.linuxtools
.tmf
.core
.signal
.TmfStatsUpdatedSignal
;
31 import org
.eclipse
.linuxtools
.tmf
.core
.statesystem
.ITmfStateProvider
;
32 import org
.eclipse
.linuxtools
.tmf
.core
.statesystem
.ITmfStateSystem
;
33 import org
.eclipse
.linuxtools
.tmf
.core
.statesystem
.TmfStateSystemFactory
;
34 import org
.eclipse
.linuxtools
.tmf
.core
.trace
.ITmfTrace
;
37 * Implementation of ITmfStatistics which uses a state history for storing its
38 * information. In reality, it uses two state histories, one for "event totals"
39 * information (which should ideally use a fast backend), and another one for
40 * the rest (per event type, per CPU, etc.).
42 * Compared to the event-request-based statistics calculations, it adds the
43 * building the history first, but gives much faster response times once built :
44 * Queries are O(log n) wrt the size of the trace, and O(1) wrt to the size of
45 * the time interval selected.
47 * @author Alexandre Montplaisir
50 public class TmfStateStatistics
implements ITmfStatistics
{
52 // ------------------------------------------------------------------------
54 // ------------------------------------------------------------------------
56 private final ITmfTrace trace
;
58 /** The event totals state system */
59 private final ITmfStateSystem totalsStats
;
61 /** The state system for event types */
62 private final ITmfStateSystem typesStats
;
64 // ------------------------------------------------------------------------
66 // ------------------------------------------------------------------------
72 * The trace for which we build these statistics
74 * The state system containing the "totals" information
76 * The state system containing the "event types" information
79 public TmfStateStatistics(@NonNull ITmfTrace trace
,
80 @NonNull ITmfStateSystem totals
,
81 @NonNull ITmfStateSystem eventTypes
) {
83 this.totalsStats
= totals
;
84 this.typesStats
= eventTypes
;
88 * Manual constructor. This should be used if the trace's Resource is null
89 * (ie, for unit tests). It requires specifying the location of the history
93 * The trace for which we build these statistics
94 * @param totalsHistoryFile
95 * The location of the totals state history file
96 * @param typesHistoryFile
97 * The location of the types state history file
98 * @throws TmfTraceException
99 * If the file could not be written to
103 public TmfStateStatistics(ITmfTrace trace
, File totalsHistoryFile
,
104 File typesHistoryFile
) throws TmfTraceException
{
106 final ITmfStateProvider totalsInput
= new TmfStatisticsTotalsModule().new StatsProviderTotals(trace
);
107 final ITmfStateProvider typesInput
= new TmfStatisticsEventTypesModule().new StatsProviderEventTypes(trace
);
108 this.totalsStats
= TmfStateSystemFactory
.newFullHistory(totalsHistoryFile
, totalsInput
, true);
109 this.typesStats
= TmfStateSystemFactory
.newFullHistory(typesHistoryFile
, typesInput
, true);
113 * Return the state system containing the "totals" values
115 * @return The "totals" state system
118 public ITmfStateSystem
getTotalsSS() {
123 * Return the state system containing the "event types" values
125 * @return The "event types" state system
128 public ITmfStateSystem
getEventTypesSS() {
132 // ------------------------------------------------------------------------
134 // ------------------------------------------------------------------------
137 public void dispose() {
138 totalsStats
.dispose();
139 typesStats
.dispose();
143 public void updateStats(final boolean isGlobal
, final long start
,
146 * Since we are currently in a signal handler (ie, in the UI thread),
147 * and since state system queries can be arbitrarily long (O(log n) wrt
148 * the size of the trace), we will run those queries in a separate
149 * thread and update the statistics view out-of-band.
151 Thread statsThread
= new Thread("Statistics update") { //$NON-NLS-1$
154 /* Wait until the history building is completed */
155 if (!waitUntilBuilt()) {
159 /* Range should be valid for both global and time range queries */
160 long total
= getEventsInRange(start
, end
);
161 Map
<String
, Long
> map
= getEventTypesInRange(start
, end
);
163 /* Send the signal to notify the stats viewer to update its display */
164 TmfSignal sig
= new TmfStatsUpdatedSignal(this, trace
, isGlobal
, total
, map
);
165 TmfSignalManager
.dispatchSignal(sig
);
173 public List
<Long
> histogramQuery(final long start
, final long end
, final int nb
) {
174 final List
<Long
> list
= new LinkedList
<>();
175 final long increment
= (end
- start
) / nb
;
177 totalsStats
.waitUntilBuilt();
178 if (totalsStats
.isCancelled()) {
183 * We will do one state system query per "border", and save the
184 * differences between each border.
186 long prevTotal
= (start
== totalsStats
.getStartTime()) ?
0 : getEventCountAt(start
);
187 long curTime
= start
+ increment
;
189 long curTotal
, count
;
190 for (int i
= 0; i
< nb
- 1; i
++) {
191 curTotal
= getEventCountAt(curTime
);
192 count
= curTotal
- prevTotal
;
195 curTime
+= increment
;
196 prevTotal
= curTotal
;
200 * For the last bucket, we'll stretch its end time to the end time of
201 * the requested range, in case it got truncated down.
203 curTotal
= getEventCountAt(end
);
204 count
= curTotal
- prevTotal
;
211 public long getEventsTotal() {
212 /* We need the complete state history to be built to answer this. */
213 totalsStats
.waitUntilBuilt();
215 long endTime
= totalsStats
.getCurrentEndTime();
219 final int quark
= totalsStats
.getQuarkAbsolute(Attributes
.TOTAL
);
220 count
= totalsStats
.querySingleState(endTime
, quark
).getStateValue().unboxInt();
222 } catch (TimeRangeException e
) {
223 /* Assume there is no events for that range */
225 } catch (AttributeNotFoundException e
) {
227 } catch (StateValueTypeException e
) {
229 } catch (StateSystemDisposedException e
) {
237 public Map
<String
, Long
> getEventTypesTotal() {
238 /* We need the complete state history to be built to answer this. */
239 typesStats
.waitUntilBuilt();
241 Map
<String
, Long
> map
= new HashMap
<>();
242 long endTime
= typesStats
.getCurrentEndTime();
245 /* Get the list of quarks, one for each even type in the database */
246 int quark
= typesStats
.getQuarkAbsolute(Attributes
.EVENT_TYPES
);
247 List
<Integer
> quarks
= typesStats
.getSubAttributes(quark
, false);
249 /* Since we want the total we can look only at the end */
250 List
<ITmfStateInterval
> endState
= typesStats
.queryFullState(endTime
);
254 for (int typeQuark
: quarks
) {
255 curEventName
= typesStats
.getAttributeName(typeQuark
);
256 eventCount
= endState
.get(typeQuark
).getStateValue().unboxInt();
257 map
.put(curEventName
, eventCount
);
260 } catch (TimeRangeException e
) {
261 /* Assume there is no events, nothing will be put in the map. */
262 } catch (AttributeNotFoundException
| StateValueTypeException
| StateSystemDisposedException e
) {
269 public long getEventsInRange(long start
, long end
) {
270 // FIXME Instead of waiting until the end, we could check the current
271 // end time, and answer as soon as possible...
272 totalsStats
.waitUntilBuilt();
275 if (start
== totalsStats
.getStartTime()) {
279 * We want the events happening at "start" to be included, so we'll
280 * need to query one unit before that point.
282 startCount
= getEventCountAt(start
- 1);
284 long endCount
= getEventCountAt(end
);
286 return endCount
- startCount
;
290 public Map
<String
, Long
> getEventTypesInRange(long start
, long end
) {
291 // FIXME Instead of waiting until the end, we could check the current
292 // end time, and answer as soon as possible...
293 typesStats
.waitUntilBuilt();
295 Map
<String
, Long
> map
= new HashMap
<>();
297 /* Make sure the start/end times are within the state history, so we
298 * don't get TimeRange exceptions.
300 long startTime
= checkStartTime(start
);
301 long endTime
= checkEndTime(end
);
304 /* Get the list of quarks, one for each even type in the database */
305 int quark
= typesStats
.getQuarkAbsolute(Attributes
.EVENT_TYPES
);
306 List
<Integer
> quarks
= typesStats
.getSubAttributes(quark
, false);
308 List
<ITmfStateInterval
> endState
= typesStats
.queryFullState(endTime
);
311 long countAtStart
, countAtEnd
, eventCount
;
313 if (startTime
== typesStats
.getStartTime()) {
314 /* Only use the values picked up at the end time */
315 for (int typeQuark
: quarks
) {
316 curEventName
= typesStats
.getAttributeName(typeQuark
);
317 eventCount
= endState
.get(typeQuark
).getStateValue().unboxInt();
318 if (eventCount
== -1) {
321 map
.put(curEventName
, eventCount
);
325 * Query the start time at -1, so the beginning of the interval
328 List
<ITmfStateInterval
> startState
= typesStats
.queryFullState(startTime
- 1);
329 for (int typeQuark
: quarks
) {
330 curEventName
= typesStats
.getAttributeName(typeQuark
);
331 countAtStart
= startState
.get(typeQuark
).getStateValue().unboxInt();
332 countAtEnd
= endState
.get(typeQuark
).getStateValue().unboxInt();
334 if (countAtStart
== -1) {
337 if (countAtEnd
== -1) {
340 eventCount
= countAtEnd
- countAtStart
;
341 map
.put(curEventName
, eventCount
);
345 } catch (TimeRangeException e
) {
346 /* Assume there is no events, nothing will be put in the map. */
347 } catch (AttributeNotFoundException
| StateValueTypeException
| StateSystemDisposedException e
) {
349 * These other exception types would show a logic problem however,
350 * so they should not happen.
357 // ------------------------------------------------------------------------
359 // ------------------------------------------------------------------------
361 private long getEventCountAt(long timestamp
) {
362 /* Make sure the target time is within the range of the history */
363 long ts
= checkStartTime(timestamp
);
364 ts
= checkEndTime(ts
);
367 final int quark
= totalsStats
.getQuarkAbsolute(Attributes
.TOTAL
);
368 long count
= totalsStats
.querySingleState(ts
, quark
).getStateValue().unboxInt();
371 } catch (TimeRangeException e
) {
372 /* Assume there is no events for that range */
373 } catch (AttributeNotFoundException
| StateValueTypeException
| StateSystemDisposedException e
) {
380 private long checkStartTime(long initialStart
) {
381 long start
= initialStart
;
382 if (start
< totalsStats
.getStartTime()) {
383 return totalsStats
.getStartTime();
388 private long checkEndTime(long initialEnd
) {
389 long end
= initialEnd
;
390 if (end
> totalsStats
.getCurrentEndTime()) {
391 return totalsStats
.getCurrentEndTime();
397 * Wait until both backing state systems are finished building.
399 * @return If both state systems were built successfully
401 private boolean waitUntilBuilt() {
402 totalsStats
.waitUntilBuilt();
403 typesStats
.waitUntilBuilt();
404 boolean check1
= !totalsStats
.isCancelled();
405 boolean check2
= !typesStats
.isCancelled();
406 return (check1
&& check2
);
411 * The attribute names that are used in the state provider
413 public static class Attributes
{
415 /** Total nb of events */
416 public static final String TOTAL
= "total"; //$NON-NLS-1$
419 public static final String EVENT_TYPES
= "event_types"; //$NON-NLS-1$<