tmf: Add the Histogram query API to ITmfStatistics
[deliverable/tracecompass.git] / org.eclipse.linuxtools.tmf.core / src / org / eclipse / linuxtools / tmf / core / statistics / TmfStateStatistics.java
1 /*******************************************************************************
2 * Copyright (c) 2012 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.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;
37
38 /**
39 * Implementation of ITmfStatistics which uses a state history for storing its
40 * information.
41 *
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.
45 *
46 * @author Alexandre Montplaisir
47 * @since 2.0
48 */
49 public class TmfStateStatistics implements ITmfStatistics {
50
51 /** ID for the statistics state system */
52 public static final String STATE_ID = "org.eclipse.linuxtools.tmf.statistics"; //$NON-NLS-1$
53
54 /** Filename the "statistics state history" file will have */
55 private static final String STATS_STATE_FILENAME = "statistics.ht"; //$NON-NLS-1$
56
57 private final ITmfTrace trace;
58
59 /**
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!)
63 */
64 private final ITmfStateSystem stats;
65
66 /**
67 * Empty constructor. The resulting TmfStatistics object will not be usable,
68 * but it might be needed for sub-classes.
69 */
70 public TmfStateStatistics() {
71 stats = null;
72 trace = null;
73 }
74
75 /**
76 * Constructor
77 *
78 * @param trace
79 * The trace for which we build these statistics
80 * @throws TmfTraceException
81 * If something went wrong trying to initialize the statistics
82 */
83 public TmfStateStatistics(ITmfTrace trace) throws TmfTraceException {
84 /* Set up the path to the history tree file we'll use */
85 this.trace = trace;
86 IResource resource = trace.getResource();
87 String supplDirectory = null;
88
89 try {
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);
94 }
95
96 final File htFile = new File(supplDirectory + File.separator + STATS_STATE_FILENAME);
97 final IStateChangeInput htInput = new StatsStateProvider(trace);
98
99 this.stats = StateSystemManager.loadStateHistory(htFile, htInput, STATE_ID, false);
100 }
101
102 /**
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
105 * file manually.
106 *
107 * @param trace
108 * The trace for which we build these statistics
109 * @param historyFile
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
113 */
114 public TmfStateStatistics(ITmfTrace trace, File historyFile) throws TmfTraceException {
115 this.trace = trace;
116 final IStateChangeInput htInput = new StatsStateProvider(trace);
117 this.stats = StateSystemManager.loadStateHistory(historyFile, htInput, STATE_ID, true);
118 }
119
120 // ------------------------------------------------------------------------
121 // ITmfStatistics
122 // ------------------------------------------------------------------------
123
124 @Override
125 public void dispose() {
126 stats.dispose();
127 }
128
129 @Override
130 public void updateStats(final boolean isGlobal, final long start,
131 final long end) {
132 /*
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.
137 */
138 Thread statsThread = new Thread("Statistics update") { //$NON-NLS-1$
139 @Override
140 public void run() {
141 long total;
142 Map<String, Long> map;
143
144 /* Wait until the history building completed */
145 if (!stats.waitUntilBuilt()) {
146 return;
147 }
148
149 /* Range should be valid for both global and time range queries */
150 total = getEventsInRange(start, end);
151 map = getEventTypesInRange(start, end);
152
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);
156 }
157 };
158 statsThread.start();
159 return;
160 }
161
162 @Override
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;
166
167 /* Wait until the history building completed */
168 if (!stats.waitUntilBuilt()) {
169 return null;
170 }
171
172 /*
173 * We will do one state system query per "border", and save the
174 * differences between each border.
175 */
176 long prevTotal = (start == stats.getStartTime()) ? 0 : getEventCountAt(start);
177 long curTime = start + increment;
178
179 long curTotal, count;
180 for (int i = 0; i < nb - 1; i++) {
181 curTotal = getEventCountAt(curTime);
182 count = curTotal - prevTotal;
183 list.add(count);
184
185 curTime += increment;
186 prevTotal = curTotal;
187 }
188
189 /*
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.
192 */
193 curTotal = getEventCountAt(end);
194 count = curTotal - prevTotal;
195 list.add(count);
196
197 return list;
198 }
199
200 @Override
201 public long getEventsTotal() {
202 /* We need the complete state history to be built to answer this. */
203 stats.waitUntilBuilt();
204
205 long endTime = stats.getCurrentEndTime();
206 int count = 0;
207
208 try {
209 final int quark = stats.getQuarkAbsolute(Attributes.TOTAL);
210 count= stats.querySingleState(endTime, quark).getStateValue().unboxInt();
211
212 } catch (TimeRangeException e) {
213 /* Assume there is no events for that range */
214 return 0;
215 } catch (AttributeNotFoundException e) {
216 e.printStackTrace();
217 } catch (StateValueTypeException e) {
218 e.printStackTrace();
219 } catch (StateSystemDisposedException e) {
220 e.printStackTrace();
221 }
222
223 return count;
224 }
225
226 @Override
227 public Map<String, Long> getEventTypesTotal() {
228 /* We need the complete state history to be built to answer this. */
229 stats.waitUntilBuilt();
230
231 Map<String, Long> map = new HashMap<String, Long>();
232 long endTime = stats.getCurrentEndTime();
233
234 try {
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);
238
239 /* Since we want the total we can look only at the end */
240 List<ITmfStateInterval> endState = stats.queryFullState(endTime);
241
242 String curEventName;
243 long eventCount;
244 for (int typeQuark : quarks) {
245 curEventName = stats.getAttributeName(typeQuark);
246 eventCount = endState.get(typeQuark).getStateValue().unboxInt();
247 map.put(curEventName, eventCount);
248 }
249
250 } catch (TimeRangeException e) {
251 /* Assume there is no events, nothing will be put in the map. */
252 } catch (AttributeNotFoundException e) {
253 e.printStackTrace();
254 } catch (StateValueTypeException e) {
255 e.printStackTrace();
256 } catch (StateSystemDisposedException e) {
257 e.printStackTrace();
258 }
259 return map;
260 }
261
262 @Override
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();
267
268 long startCount;
269 if (start == stats.getStartTime()) {
270 startCount = 0;
271 } else {
272 /*
273 * We want the events happening at "start" to be included, so we'll
274 * need to query one unit before that point.
275 */
276 startCount = getEventCountAt(start - 1);
277 }
278 long endCount = getEventCountAt(end);
279
280 return endCount - startCount;
281 }
282
283 @Override
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();
288
289 Map<String, Long> map = new HashMap<String, Long>();
290
291 /* Make sure the start/end times are within the state history, so we
292 * don't get TimeRange exceptions.
293 */
294 long startTime = checkStartTime(start);
295 long endTime = checkEndTime(end);
296
297 try {
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);
301
302 List<ITmfStateInterval> endState = stats.queryFullState(endTime);
303
304 String curEventName;
305 long countAtStart, countAtEnd, eventCount;
306
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) {
313 eventCount = 0;
314 }
315 map.put(curEventName, eventCount);
316 }
317 } else {
318 /*
319 * Query the start time at -1, so the beginning of the interval
320 * is inclusive.
321 */
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();
327
328 if (countAtStart == -1) {
329 countAtStart = 0;
330 }
331 if (countAtEnd == -1) {
332 countAtEnd = 0;
333 }
334 eventCount = countAtEnd - countAtStart;
335 map.put(curEventName, eventCount);
336 }
337 }
338
339 } catch (TimeRangeException e) {
340 /* Assume there is no events, nothing will be put in the map. */
341 } catch (AttributeNotFoundException e) {
342 /*
343 * These other exception types would show a logic problem however,
344 * so they should not happen.
345 */
346 e.printStackTrace();
347 } catch (StateValueTypeException e) {
348 e.printStackTrace();
349 } catch (StateSystemDisposedException e) {
350 e.printStackTrace();
351 }
352 return map;
353 }
354
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);
359
360 try {
361 final int quark = stats.getQuarkAbsolute(Attributes.TOTAL);
362 long count = stats.querySingleState(ts, quark).getStateValue().unboxInt();
363 return count;
364
365 } catch (TimeRangeException e) {
366 /* Assume there is no events for that range */
367 } catch (AttributeNotFoundException e) {
368 e.printStackTrace();
369 } catch (StateValueTypeException e) {
370 e.printStackTrace();
371 } catch (StateSystemDisposedException e) {
372 e.printStackTrace();
373 }
374
375 return 0;
376 }
377
378 private long checkStartTime(long initialStart) {
379 long start = initialStart;
380 if (start < stats.getStartTime()) {
381 return stats.getStartTime();
382 }
383 return start;
384 }
385
386 private long checkEndTime(long initialEnd) {
387 long end = initialEnd;
388 if (end > stats.getCurrentEndTime()) {
389 return stats.getCurrentEndTime();
390 }
391 return end;
392 }
393
394
395 /**
396 * The attribute names that are used in the state provider
397 */
398 public static class Attributes {
399
400 /** Total nb of events */
401 public static final String TOTAL = "total"; //$NON-NLS-1$
402
403 /** event_types */
404 public static final String EVENT_TYPES = "event_types"; //$NON-NLS-1$<
405 }
406 }
This page took 0.039239 seconds and 6 git commands to generate.