b43a622f5ad162a59a3fc21a29d42c853a228014
[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.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;
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 // Fields
54 // ------------------------------------------------------------------------
55
56 private final ITmfTrace trace;
57
58 /** The event totals state system */
59 private final ITmfStateSystem totalsStats;
60
61 /** The state system for event types */
62 private final ITmfStateSystem typesStats;
63
64 // ------------------------------------------------------------------------
65 // Constructors
66 // ------------------------------------------------------------------------
67
68 /**
69 * Constructor
70 *
71 * @param trace
72 * The trace for which we build these statistics
73 * @param totals
74 * The state system containing the "totals" information
75 * @param eventTypes
76 * The state system containing the "event types" information
77 * @since 3.0
78 */
79 public TmfStateStatistics(@NonNull ITmfTrace trace,
80 @NonNull ITmfStateSystem totals,
81 @NonNull ITmfStateSystem eventTypes) {
82 this.trace = trace;
83 this.totalsStats = totals;
84 this.typesStats = eventTypes;
85 }
86
87 /**
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
90 * files manually.
91 *
92 * @param trace
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
100 * @since 2.2
101 */
102 @Deprecated
103 public TmfStateStatistics(ITmfTrace trace, File totalsHistoryFile,
104 File typesHistoryFile) throws TmfTraceException {
105 this.trace = trace;
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);
110 }
111
112 /**
113 * Return the state system containing the "totals" values
114 *
115 * @return The "totals" state system
116 * @since 3.0
117 */
118 public ITmfStateSystem getTotalsSS() {
119 return totalsStats;
120 }
121
122 /**
123 * Return the state system containing the "event types" values
124 *
125 * @return The "event types" state system
126 * @since 3.0
127 */
128 public ITmfStateSystem getEventTypesSS() {
129 return typesStats;
130 }
131
132 // ------------------------------------------------------------------------
133 // ITmfStatistics
134 // ------------------------------------------------------------------------
135
136 @Override
137 public void dispose() {
138 totalsStats.dispose();
139 typesStats.dispose();
140 }
141
142 @Override
143 public void updateStats(final boolean isGlobal, final long start,
144 final long end) {
145 /*
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.
150 */
151 Thread statsThread = new Thread("Statistics update") { //$NON-NLS-1$
152 @Override
153 public void run() {
154 /* Wait until the history building is completed */
155 if (!waitUntilBuilt()) {
156 return;
157 }
158
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);
162
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);
166 }
167 };
168 statsThread.start();
169 return;
170 }
171
172 @Override
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;
176
177 totalsStats.waitUntilBuilt();
178 if (totalsStats.isCancelled()) {
179 return list;
180 }
181
182 /*
183 * We will do one state system query per "border", and save the
184 * differences between each border.
185 */
186 long prevTotal = (start == totalsStats.getStartTime()) ? 0 : getEventCountAt(start);
187 long curTime = start + increment;
188
189 long curTotal, count;
190 for (int i = 0; i < nb - 1; i++) {
191 curTotal = getEventCountAt(curTime);
192 count = curTotal - prevTotal;
193 list.add(count);
194
195 curTime += increment;
196 prevTotal = curTotal;
197 }
198
199 /*
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.
202 */
203 curTotal = getEventCountAt(end);
204 count = curTotal - prevTotal;
205 list.add(count);
206
207 return list;
208 }
209
210 @Override
211 public long getEventsTotal() {
212 /* We need the complete state history to be built to answer this. */
213 totalsStats.waitUntilBuilt();
214
215 long endTime = totalsStats.getCurrentEndTime();
216 int count = 0;
217
218 try {
219 final int quark = totalsStats.getQuarkAbsolute(Attributes.TOTAL);
220 count= totalsStats.querySingleState(endTime, quark).getStateValue().unboxInt();
221
222 } catch (TimeRangeException e) {
223 /* Assume there is no events for that range */
224 return 0;
225 } catch (AttributeNotFoundException e) {
226 e.printStackTrace();
227 } catch (StateValueTypeException e) {
228 e.printStackTrace();
229 } catch (StateSystemDisposedException e) {
230 e.printStackTrace();
231 }
232
233 return count;
234 }
235
236 @Override
237 public Map<String, Long> getEventTypesTotal() {
238 /* We need the complete state history to be built to answer this. */
239 typesStats.waitUntilBuilt();
240
241 Map<String, Long> map = new HashMap<>();
242 long endTime = typesStats.getCurrentEndTime();
243
244 try {
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);
248
249 /* Since we want the total we can look only at the end */
250 List<ITmfStateInterval> endState = typesStats.queryFullState(endTime);
251
252 String curEventName;
253 long eventCount;
254 for (int typeQuark : quarks) {
255 curEventName = typesStats.getAttributeName(typeQuark);
256 eventCount = endState.get(typeQuark).getStateValue().unboxInt();
257 map.put(curEventName, eventCount);
258 }
259
260 } catch (TimeRangeException e) {
261 /* Assume there is no events, nothing will be put in the map. */
262 } catch (AttributeNotFoundException | StateValueTypeException | StateSystemDisposedException e) {
263 e.printStackTrace();
264 }
265 return map;
266 }
267
268 @Override
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();
273
274 long startCount;
275 if (start == totalsStats.getStartTime()) {
276 startCount = 0;
277 } else {
278 /*
279 * We want the events happening at "start" to be included, so we'll
280 * need to query one unit before that point.
281 */
282 startCount = getEventCountAt(start - 1);
283 }
284 long endCount = getEventCountAt(end);
285
286 return endCount - startCount;
287 }
288
289 @Override
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();
294
295 Map<String, Long> map = new HashMap<>();
296
297 /* Make sure the start/end times are within the state history, so we
298 * don't get TimeRange exceptions.
299 */
300 long startTime = checkStartTime(start);
301 long endTime = checkEndTime(end);
302
303 try {
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);
307
308 List<ITmfStateInterval> endState = typesStats.queryFullState(endTime);
309
310 String curEventName;
311 long countAtStart, countAtEnd, eventCount;
312
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) {
319 eventCount = 0;
320 }
321 map.put(curEventName, eventCount);
322 }
323 } else {
324 /*
325 * Query the start time at -1, so the beginning of the interval
326 * is inclusive.
327 */
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();
333
334 if (countAtStart == -1) {
335 countAtStart = 0;
336 }
337 if (countAtEnd == -1) {
338 countAtEnd = 0;
339 }
340 eventCount = countAtEnd - countAtStart;
341 map.put(curEventName, eventCount);
342 }
343 }
344
345 } catch (TimeRangeException e) {
346 /* Assume there is no events, nothing will be put in the map. */
347 } catch (AttributeNotFoundException | StateValueTypeException | StateSystemDisposedException e) {
348 /*
349 * These other exception types would show a logic problem however,
350 * so they should not happen.
351 */
352 e.printStackTrace();
353 }
354 return map;
355 }
356
357 // ------------------------------------------------------------------------
358 // Helper methods
359 // ------------------------------------------------------------------------
360
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);
365
366 try {
367 final int quark = totalsStats.getQuarkAbsolute(Attributes.TOTAL);
368 long count = totalsStats.querySingleState(ts, quark).getStateValue().unboxInt();
369 return count;
370
371 } catch (TimeRangeException e) {
372 /* Assume there is no events for that range */
373 } catch (AttributeNotFoundException | StateValueTypeException | StateSystemDisposedException e) {
374 e.printStackTrace();
375 }
376
377 return 0;
378 }
379
380 private long checkStartTime(long initialStart) {
381 long start = initialStart;
382 if (start < totalsStats.getStartTime()) {
383 return totalsStats.getStartTime();
384 }
385 return start;
386 }
387
388 private long checkEndTime(long initialEnd) {
389 long end = initialEnd;
390 if (end > totalsStats.getCurrentEndTime()) {
391 return totalsStats.getCurrentEndTime();
392 }
393 return end;
394 }
395
396 /**
397 * Wait until both backing state systems are finished building.
398 *
399 * @return If both state systems were built successfully
400 */
401 private boolean waitUntilBuilt() {
402 totalsStats.waitUntilBuilt();
403 typesStats.waitUntilBuilt();
404 boolean check1 = !totalsStats.isCancelled();
405 boolean check2 = !typesStats.isCancelled();
406 return (check1 && check2);
407 }
408
409
410 /**
411 * The attribute names that are used in the state provider
412 */
413 public static class Attributes {
414
415 /** Total nb of events */
416 public static final String TOTAL = "total"; //$NON-NLS-1$
417
418 /** event_types */
419 public static final String EVENT_TYPES = "event_types"; //$NON-NLS-1$<
420 }
421 }
This page took 0.039826 seconds and 5 git commands to generate.