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
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 // ------------------------------------------------------------------------
57 * @deprecated Do not use, it's been replaced by {@link #TOTALS_STATE_ID}
58 * and {@link #TYPES_STATE_ID}
61 public static final String STATE_ID
= "org.eclipse.linuxtools.tmf.statistics"; //$NON-NLS-1$
63 /** ID for the "event totals" statistics state system
65 public static final String TOTALS_STATE_ID
= "org.eclipse.linuxtools.tmf.statistics.totals"; //$NON-NLS-1$
67 /** ID for the "event types" statistics state system
69 public static final String TYPES_STATE_ID
= "org.eclipse.linuxtools.tmf.statistics.types"; //$NON-NLS-1$
71 /** Filename for the "event totals" state history file */
72 private static final String TOTALS_STATE_FILENAME
= "statistics-totals.ht"; //$NON-NLS-1$
74 /** Filename for the "event types" state history file */
75 private static final String TYPES_STATE_FILENAME
= "statistics-types.ht"; //$NON-NLS-1$
77 // ------------------------------------------------------------------------
79 // ------------------------------------------------------------------------
81 private final ITmfTrace trace
;
83 /** The event totals state system */
84 private final ITmfStateSystem totalsStats
;
86 /** The state system for event types */
87 private final ITmfStateSystem typesStats
;
89 // ------------------------------------------------------------------------
91 // ------------------------------------------------------------------------
94 * Empty constructor. The resulting TmfStatistics object will not be usable,
95 * but it might be needed for sub-classes.
97 public TmfStateStatistics() {
107 * The trace for which we build these statistics
108 * @throws TmfTraceException
109 * If something went wrong trying to initialize the statistics
111 public TmfStateStatistics(ITmfTrace trace
) throws TmfTraceException
{
113 String directory
= TmfTraceManager
.getSupplementaryFileDir(trace
);
115 final File totalsFile
= new File(directory
+ TOTALS_STATE_FILENAME
);
116 final ITmfStateProvider totalsInput
= new StatsProviderTotals(trace
);
117 this.totalsStats
= TmfStateSystemFactory
.newFullHistory(totalsFile
, totalsInput
, false);
119 final File typesFile
= new File(directory
+ TYPES_STATE_FILENAME
);
120 final ITmfStateProvider typesInput
= new StatsProviderEventTypes(trace
);
121 this.typesStats
= TmfStateSystemFactory
.newFullHistory(typesFile
, typesInput
, false);
123 registerStateSystems();
127 * Old manual constructor.
130 * @param historyFile Full history file
131 * @deprecated Need to use {@link #TmfStateStatistics(ITmfTrace trace,
132 * File fullHistoryFile, File partialHistoryFile)} now.
135 public TmfStateStatistics(ITmfTrace trace
, File historyFile
) {
140 * Manual constructor. This should be used if the trace's Resource is null
141 * (ie, for unit tests). It requires specifying the location of the history
145 * The trace for which we build these statistics
146 * @param totalsHistoryFile
147 * The location of the totals state history file
148 * @param typesHistoryFile
149 * The location of the types state history file
150 * @throws TmfTraceException
151 * If the file could not be written to
154 public TmfStateStatistics(ITmfTrace trace
, File totalsHistoryFile
,
155 File typesHistoryFile
) throws TmfTraceException
{
157 final ITmfStateProvider totalsInput
= new StatsProviderTotals(trace
);
158 final ITmfStateProvider typesInput
= new StatsProviderEventTypes(trace
);
159 this.totalsStats
= TmfStateSystemFactory
.newFullHistory(totalsHistoryFile
, totalsInput
, true);
160 this.typesStats
= TmfStateSystemFactory
.newFullHistory(typesHistoryFile
, typesInput
, true);
161 registerStateSystems();
165 * Register the state systems used here into the trace's state system map.
167 private void registerStateSystems() {
168 trace
.registerStateSystem(TOTALS_STATE_ID
, totalsStats
);
169 trace
.registerStateSystem(TYPES_STATE_ID
, typesStats
);
172 // ------------------------------------------------------------------------
174 // ------------------------------------------------------------------------
177 public void dispose() {
178 totalsStats
.dispose();
179 typesStats
.dispose();
183 public void updateStats(final boolean isGlobal
, final long start
,
186 * Since we are currently in a signal handler (ie, in the UI thread),
187 * and since state system queries can be arbitrarily long (O(log n) wrt
188 * the size of the trace), we will run those queries in a separate
189 * thread and update the statistics view out-of-band.
191 Thread statsThread
= new Thread("Statistics update") { //$NON-NLS-1$
194 /* Wait until the history building is completed */
195 if (!waitUntilBuilt()) {
199 /* Range should be valid for both global and time range queries */
200 long total
= getEventsInRange(start
, end
);
201 Map
<String
, Long
> map
= getEventTypesInRange(start
, end
);
203 /* Send the signal to notify the stats viewer to update its display */
204 TmfSignal sig
= new TmfStatsUpdatedSignal(this, trace
, isGlobal
, total
, map
);
205 TmfSignalManager
.dispatchSignal(sig
);
213 public List
<Long
> histogramQuery(final long start
, final long end
, final int nb
) {
214 final List
<Long
> list
= new LinkedList
<>();
215 final long increment
= (end
- start
) / nb
;
217 totalsStats
.waitUntilBuilt();
218 if (totalsStats
.isCancelled()) {
223 * We will do one state system query per "border", and save the
224 * differences between each border.
226 long prevTotal
= (start
== totalsStats
.getStartTime()) ?
0 : getEventCountAt(start
);
227 long curTime
= start
+ increment
;
229 long curTotal
, count
;
230 for (int i
= 0; i
< nb
- 1; i
++) {
231 curTotal
= getEventCountAt(curTime
);
232 count
= curTotal
- prevTotal
;
235 curTime
+= increment
;
236 prevTotal
= curTotal
;
240 * For the last bucket, we'll stretch its end time to the end time of
241 * the requested range, in case it got truncated down.
243 curTotal
= getEventCountAt(end
);
244 count
= curTotal
- prevTotal
;
251 public long getEventsTotal() {
252 /* We need the complete state history to be built to answer this. */
253 totalsStats
.waitUntilBuilt();
255 long endTime
= totalsStats
.getCurrentEndTime();
259 final int quark
= totalsStats
.getQuarkAbsolute(Attributes
.TOTAL
);
260 count
= totalsStats
.querySingleState(endTime
, quark
).getStateValue().unboxInt();
262 } catch (TimeRangeException e
) {
263 /* Assume there is no events for that range */
265 } catch (AttributeNotFoundException e
) {
267 } catch (StateValueTypeException e
) {
269 } catch (StateSystemDisposedException e
) {
277 public Map
<String
, Long
> getEventTypesTotal() {
278 /* We need the complete state history to be built to answer this. */
279 typesStats
.waitUntilBuilt();
281 Map
<String
, Long
> map
= new HashMap
<>();
282 long endTime
= typesStats
.getCurrentEndTime();
285 /* Get the list of quarks, one for each even type in the database */
286 int quark
= typesStats
.getQuarkAbsolute(Attributes
.EVENT_TYPES
);
287 List
<Integer
> quarks
= typesStats
.getSubAttributes(quark
, false);
289 /* Since we want the total we can look only at the end */
290 List
<ITmfStateInterval
> endState
= typesStats
.queryFullState(endTime
);
294 for (int typeQuark
: quarks
) {
295 curEventName
= typesStats
.getAttributeName(typeQuark
);
296 eventCount
= endState
.get(typeQuark
).getStateValue().unboxInt();
297 map
.put(curEventName
, eventCount
);
300 } catch (TimeRangeException e
) {
301 /* Assume there is no events, nothing will be put in the map. */
302 } catch (AttributeNotFoundException e
) {
304 } catch (StateValueTypeException e
) {
306 } catch (StateSystemDisposedException e
) {
313 public long getEventsInRange(long start
, long end
) {
314 // FIXME Instead of waiting until the end, we could check the current
315 // end time, and answer as soon as possible...
316 totalsStats
.waitUntilBuilt();
319 if (start
== totalsStats
.getStartTime()) {
323 * We want the events happening at "start" to be included, so we'll
324 * need to query one unit before that point.
326 startCount
= getEventCountAt(start
- 1);
328 long endCount
= getEventCountAt(end
);
330 return endCount
- startCount
;
334 public Map
<String
, Long
> getEventTypesInRange(long start
, long end
) {
335 // FIXME Instead of waiting until the end, we could check the current
336 // end time, and answer as soon as possible...
337 typesStats
.waitUntilBuilt();
339 Map
<String
, Long
> map
= new HashMap
<>();
341 /* Make sure the start/end times are within the state history, so we
342 * don't get TimeRange exceptions.
344 long startTime
= checkStartTime(start
);
345 long endTime
= checkEndTime(end
);
348 /* Get the list of quarks, one for each even type in the database */
349 int quark
= typesStats
.getQuarkAbsolute(Attributes
.EVENT_TYPES
);
350 List
<Integer
> quarks
= typesStats
.getSubAttributes(quark
, false);
352 List
<ITmfStateInterval
> endState
= typesStats
.queryFullState(endTime
);
355 long countAtStart
, countAtEnd
, eventCount
;
357 if (startTime
== typesStats
.getStartTime()) {
358 /* Only use the values picked up at the end time */
359 for (int typeQuark
: quarks
) {
360 curEventName
= typesStats
.getAttributeName(typeQuark
);
361 eventCount
= endState
.get(typeQuark
).getStateValue().unboxInt();
362 if (eventCount
== -1) {
365 map
.put(curEventName
, eventCount
);
369 * Query the start time at -1, so the beginning of the interval
372 List
<ITmfStateInterval
> startState
= typesStats
.queryFullState(startTime
- 1);
373 for (int typeQuark
: quarks
) {
374 curEventName
= typesStats
.getAttributeName(typeQuark
);
375 countAtStart
= startState
.get(typeQuark
).getStateValue().unboxInt();
376 countAtEnd
= endState
.get(typeQuark
).getStateValue().unboxInt();
378 if (countAtStart
== -1) {
381 if (countAtEnd
== -1) {
384 eventCount
= countAtEnd
- countAtStart
;
385 map
.put(curEventName
, eventCount
);
389 } catch (TimeRangeException e
) {
390 /* Assume there is no events, nothing will be put in the map. */
391 } catch (AttributeNotFoundException e
) {
393 * These other exception types would show a logic problem however,
394 * so they should not happen.
397 } catch (StateValueTypeException e
) {
399 } catch (StateSystemDisposedException e
) {
405 // ------------------------------------------------------------------------
407 // ------------------------------------------------------------------------
409 private long getEventCountAt(long timestamp
) {
410 /* Make sure the target time is within the range of the history */
411 long ts
= checkStartTime(timestamp
);
412 ts
= checkEndTime(ts
);
415 final int quark
= totalsStats
.getQuarkAbsolute(Attributes
.TOTAL
);
416 long count
= totalsStats
.querySingleState(ts
, quark
).getStateValue().unboxInt();
419 } catch (TimeRangeException e
) {
420 /* Assume there is no events for that range */
421 } catch (AttributeNotFoundException e
) {
423 } catch (StateValueTypeException e
) {
425 } catch (StateSystemDisposedException e
) {
432 private long checkStartTime(long initialStart
) {
433 long start
= initialStart
;
434 if (start
< totalsStats
.getStartTime()) {
435 return totalsStats
.getStartTime();
440 private long checkEndTime(long initialEnd
) {
441 long end
= initialEnd
;
442 if (end
> totalsStats
.getCurrentEndTime()) {
443 return totalsStats
.getCurrentEndTime();
449 * Wait until both backing state systems are finished building.
451 * @return If both state systems were built successfully
453 private boolean waitUntilBuilt() {
454 totalsStats
.waitUntilBuilt();
455 typesStats
.waitUntilBuilt();
456 boolean check1
= !totalsStats
.isCancelled();
457 boolean check2
= !typesStats
.isCancelled();
458 return (check1
&& check2
);
463 * The attribute names that are used in the state provider
465 public static class Attributes
{
467 /** Total nb of events */
468 public static final String TOTAL
= "total"; //$NON-NLS-1$
471 public static final String EVENT_TYPES
= "event_types"; //$NON-NLS-1$<