1a2ddedfc1d66484d9dd64f25bf9859f36c7193f
[deliverable/tracecompass.git] / org.eclipse.linuxtools.tmf.core / src / org / eclipse / linuxtools / tmf / core / statistics / TmfStateStatistics.java
1 /*******************************************************************************
2 * Copyright (c) 2012, 2013 Ericsson
3 *
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
8 *
9 * Contributors:
10 * Alexandre Montplaisir - Initial API and implementation
11 ******************************************************************************/
12
13 package org.eclipse.linuxtools.tmf.core.statistics;
14
15 import java.io.File;
16 import java.util.HashMap;
17 import java.util.LinkedList;
18 import java.util.List;
19 import java.util.Map;
20
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;
35
36 /**
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.).
41 *
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.
46 *
47 * @author Alexandre Montplaisir
48 * @since 2.0
49 */
50 public class TmfStateStatistics implements ITmfStatistics {
51
52 // ------------------------------------------------------------------------
53 // Constants
54 // ------------------------------------------------------------------------
55
56 /**
57 * @deprecated Do not use, it's been replaced by {@link #TOTALS_STATE_ID}
58 *  and {@link #TYPES_STATE_ID}
59 */
60 @Deprecated
61 public static final String STATE_ID = "org.eclipse.linuxtools.tmf.statistics"; //$NON-NLS-1$
62
63 /** ID for the "event totals" statistics state system
64 * @since 2.2 */
65 public static final String TOTALS_STATE_ID = "org.eclipse.linuxtools.tmf.statistics.totals"; //$NON-NLS-1$
66
67 /** ID for the "event types" statistics state system
68 * @since 2.2 */
69 public static final String TYPES_STATE_ID = "org.eclipse.linuxtools.tmf.statistics.types"; //$NON-NLS-1$
70
71 /** Filename for the "event totals" state history file */
72 private static final String TOTALS_STATE_FILENAME = "statistics-totals.ht"; //$NON-NLS-1$
73
74 /** Filename for the "event types" state history file */
75 private static final String TYPES_STATE_FILENAME = "statistics-types.ht"; //$NON-NLS-1$
76
77 // ------------------------------------------------------------------------
78 // Fields
79 // ------------------------------------------------------------------------
80
81 private final ITmfTrace trace;
82
83 /** The event totals state system */
84 private final ITmfStateSystem totalsStats;
85
86 /** The state system for event types */
87 private final ITmfStateSystem typesStats;
88
89 // ------------------------------------------------------------------------
90 // Constructors
91 // ------------------------------------------------------------------------
92
93 /**
94 * Empty constructor. The resulting TmfStatistics object will not be usable,
95 * but it might be needed for sub-classes.
96 */
97 public TmfStateStatistics() {
98 totalsStats = null;
99 typesStats = null;
100 trace = null;
101 }
102
103 /**
104 * Constructor
105 *
106 * @param trace
107 * The trace for which we build these statistics
108 * @throws TmfTraceException
109 * If something went wrong trying to initialize the statistics
110 */
111 public TmfStateStatistics(ITmfTrace trace) throws TmfTraceException {
112 this.trace = trace;
113 String directory = TmfTraceManager.getSupplementaryFileDir(trace);
114
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);
118
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);
122
123 registerStateSystems();
124 }
125
126 /**
127 * Old manual constructor.
128 *
129 * @param trace Trace
130 * @param historyFile Full history file
131 * @deprecated Need to use {@link #TmfStateStatistics(ITmfTrace trace,
132 * File fullHistoryFile, File partialHistoryFile)} now.
133 */
134 @Deprecated
135 public TmfStateStatistics(ITmfTrace trace, File historyFile) {
136 this();
137 }
138
139 /**
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
142 * files manually.
143 *
144 * @param trace
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
152 * @since 2.2
153 */
154 public TmfStateStatistics(ITmfTrace trace, File totalsHistoryFile,
155 File typesHistoryFile) throws TmfTraceException {
156 this.trace = trace;
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();
162 }
163
164 /**
165 * Register the state systems used here into the trace's state system map.
166 */
167 private void registerStateSystems() {
168 trace.registerStateSystem(TOTALS_STATE_ID, totalsStats);
169 trace.registerStateSystem(TYPES_STATE_ID, typesStats);
170 }
171
172 // ------------------------------------------------------------------------
173 // ITmfStatistics
174 // ------------------------------------------------------------------------
175
176 @Override
177 public void dispose() {
178 totalsStats.dispose();
179 typesStats.dispose();
180 }
181
182 @Override
183 public void updateStats(final boolean isGlobal, final long start,
184 final long end) {
185 /*
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.
190 */
191 Thread statsThread = new Thread("Statistics update") { //$NON-NLS-1$
192 @Override
193 public void run() {
194 /* Wait until the history building is completed */
195 if (!waitUntilBuilt()) {
196 return;
197 }
198
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);
202
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);
206 }
207 };
208 statsThread.start();
209 return;
210 }
211
212 @Override
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;
216
217 totalsStats.waitUntilBuilt();
218 if (totalsStats.isCancelled()) {
219 return list;
220 }
221
222 /*
223 * We will do one state system query per "border", and save the
224 * differences between each border.
225 */
226 long prevTotal = (start == totalsStats.getStartTime()) ? 0 : getEventCountAt(start);
227 long curTime = start + increment;
228
229 long curTotal, count;
230 for (int i = 0; i < nb - 1; i++) {
231 curTotal = getEventCountAt(curTime);
232 count = curTotal - prevTotal;
233 list.add(count);
234
235 curTime += increment;
236 prevTotal = curTotal;
237 }
238
239 /*
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.
242 */
243 curTotal = getEventCountAt(end);
244 count = curTotal - prevTotal;
245 list.add(count);
246
247 return list;
248 }
249
250 @Override
251 public long getEventsTotal() {
252 /* We need the complete state history to be built to answer this. */
253 totalsStats.waitUntilBuilt();
254
255 long endTime = totalsStats.getCurrentEndTime();
256 int count = 0;
257
258 try {
259 final int quark = totalsStats.getQuarkAbsolute(Attributes.TOTAL);
260 count= totalsStats.querySingleState(endTime, quark).getStateValue().unboxInt();
261
262 } catch (TimeRangeException e) {
263 /* Assume there is no events for that range */
264 return 0;
265 } catch (AttributeNotFoundException e) {
266 e.printStackTrace();
267 } catch (StateValueTypeException e) {
268 e.printStackTrace();
269 } catch (StateSystemDisposedException e) {
270 e.printStackTrace();
271 }
272
273 return count;
274 }
275
276 @Override
277 public Map<String, Long> getEventTypesTotal() {
278 /* We need the complete state history to be built to answer this. */
279 typesStats.waitUntilBuilt();
280
281 Map<String, Long> map = new HashMap<>();
282 long endTime = typesStats.getCurrentEndTime();
283
284 try {
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);
288
289 /* Since we want the total we can look only at the end */
290 List<ITmfStateInterval> endState = typesStats.queryFullState(endTime);
291
292 String curEventName;
293 long eventCount;
294 for (int typeQuark : quarks) {
295 curEventName = typesStats.getAttributeName(typeQuark);
296 eventCount = endState.get(typeQuark).getStateValue().unboxInt();
297 map.put(curEventName, eventCount);
298 }
299
300 } catch (TimeRangeException e) {
301 /* Assume there is no events, nothing will be put in the map. */
302 } catch (AttributeNotFoundException e) {
303 e.printStackTrace();
304 } catch (StateValueTypeException e) {
305 e.printStackTrace();
306 } catch (StateSystemDisposedException e) {
307 e.printStackTrace();
308 }
309 return map;
310 }
311
312 @Override
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();
317
318 long startCount;
319 if (start == totalsStats.getStartTime()) {
320 startCount = 0;
321 } else {
322 /*
323 * We want the events happening at "start" to be included, so we'll
324 * need to query one unit before that point.
325 */
326 startCount = getEventCountAt(start - 1);
327 }
328 long endCount = getEventCountAt(end);
329
330 return endCount - startCount;
331 }
332
333 @Override
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();
338
339 Map<String, Long> map = new HashMap<>();
340
341 /* Make sure the start/end times are within the state history, so we
342 * don't get TimeRange exceptions.
343 */
344 long startTime = checkStartTime(start);
345 long endTime = checkEndTime(end);
346
347 try {
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);
351
352 List<ITmfStateInterval> endState = typesStats.queryFullState(endTime);
353
354 String curEventName;
355 long countAtStart, countAtEnd, eventCount;
356
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) {
363 eventCount = 0;
364 }
365 map.put(curEventName, eventCount);
366 }
367 } else {
368 /*
369 * Query the start time at -1, so the beginning of the interval
370 * is inclusive.
371 */
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();
377
378 if (countAtStart == -1) {
379 countAtStart = 0;
380 }
381 if (countAtEnd == -1) {
382 countAtEnd = 0;
383 }
384 eventCount = countAtEnd - countAtStart;
385 map.put(curEventName, eventCount);
386 }
387 }
388
389 } catch (TimeRangeException e) {
390 /* Assume there is no events, nothing will be put in the map. */
391 } catch (AttributeNotFoundException e) {
392 /*
393 * These other exception types would show a logic problem however,
394 * so they should not happen.
395 */
396 e.printStackTrace();
397 } catch (StateValueTypeException e) {
398 e.printStackTrace();
399 } catch (StateSystemDisposedException e) {
400 e.printStackTrace();
401 }
402 return map;
403 }
404
405 // ------------------------------------------------------------------------
406 // Helper methods
407 // ------------------------------------------------------------------------
408
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);
413
414 try {
415 final int quark = totalsStats.getQuarkAbsolute(Attributes.TOTAL);
416 long count = totalsStats.querySingleState(ts, quark).getStateValue().unboxInt();
417 return count;
418
419 } catch (TimeRangeException e) {
420 /* Assume there is no events for that range */
421 } catch (AttributeNotFoundException e) {
422 e.printStackTrace();
423 } catch (StateValueTypeException e) {
424 e.printStackTrace();
425 } catch (StateSystemDisposedException e) {
426 e.printStackTrace();
427 }
428
429 return 0;
430 }
431
432 private long checkStartTime(long initialStart) {
433 long start = initialStart;
434 if (start < totalsStats.getStartTime()) {
435 return totalsStats.getStartTime();
436 }
437 return start;
438 }
439
440 private long checkEndTime(long initialEnd) {
441 long end = initialEnd;
442 if (end > totalsStats.getCurrentEndTime()) {
443 return totalsStats.getCurrentEndTime();
444 }
445 return end;
446 }
447
448 /**
449 * Wait until both backing state systems are finished building.
450 *
451 * @return If both state systems were built successfully
452 */
453 private boolean waitUntilBuilt() {
454 totalsStats.waitUntilBuilt();
455 typesStats.waitUntilBuilt();
456 boolean check1 = !totalsStats.isCancelled();
457 boolean check2 = !typesStats.isCancelled();
458 return (check1 && check2);
459 }
460
461
462 /**
463 * The attribute names that are used in the state provider
464 */
465 public static class Attributes {
466
467 /** Total nb of events */
468 public static final String TOTAL = "total"; //$NON-NLS-1$
469
470 /** event_types */
471 public static final String EVENT_TYPES = "event_types"; //$NON-NLS-1$<
472 }
473 }
This page took 0.040623 seconds and 5 git commands to generate.