tmf: Rename StateSystemManager to TmfStateSystemFactory
[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.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.ITmfStateProvider;
34 import org.eclipse.linuxtools.tmf.core.statesystem.ITmfStateSystem;
35 import org.eclipse.linuxtools.tmf.core.statesystem.TmfStateSystemFactory;
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 ITmfStateProvider htInput = new StatsStateProvider(trace);
98
99 this.stats = TmfStateSystemFactory.newFullHistory(htFile, htInput, false);
100 registerStateSystems();
101 }
102
103 /**
104 * Manual constructor. This should be used if the trace's Resource is null
105 * (ie, for unit tests). It requires specifying the location of the history
106 * file manually.
107 *
108 * @param trace
109 * The trace for which we build these statistics
110 * @param historyFile
111 * The location of the state history file to build for the stats
112 * @throws TmfTraceException
113 * If the file could not be written to
114 */
115 public TmfStateStatistics(ITmfTrace trace, File historyFile) throws TmfTraceException {
116 this.trace = trace;
117 final ITmfStateProvider htInput = new StatsStateProvider(trace);
118 this.stats = TmfStateSystemFactory.newFullHistory(historyFile, htInput, true);
119 registerStateSystems();
120 }
121
122 /**
123 * Register the state systems used here into the trace's state system array.
124 */
125 private void registerStateSystems() {
126 trace.registerStateSystem(STATE_ID, stats);
127 }
128
129 // ------------------------------------------------------------------------
130 // ITmfStatistics
131 // ------------------------------------------------------------------------
132
133 @Override
134 public void dispose() {
135 stats.dispose();
136 }
137
138 @Override
139 public void updateStats(final boolean isGlobal, final long start,
140 final long end) {
141 /*
142 * Since we are currently in a signal handler (ie, in the UI thread),
143 * and since state system queries can be arbitrarily long (O(log n) wrt
144 * the size of the trace), we will run those queries in a separate
145 * thread and update the statistics view out-of-band.
146 */
147 Thread statsThread = new Thread("Statistics update") { //$NON-NLS-1$
148 @Override
149 public void run() {
150 long total;
151 Map<String, Long> map;
152
153 /* Wait until the history building completed */
154 if (!stats.waitUntilBuilt()) {
155 return;
156 }
157
158 /* Range should be valid for both global and time range queries */
159 total = getEventsInRange(start, end);
160 map = getEventTypesInRange(start, end);
161
162 /* Send the signal to notify the stats viewer to update its display */
163 TmfSignal sig = new TmfStatsUpdatedSignal(this, trace, isGlobal, total, map);
164 TmfSignalManager.dispatchSignal(sig);
165 }
166 };
167 statsThread.start();
168 return;
169 }
170
171 @Override
172 public List<Long> histogramQuery(final long start, final long end, final int nb) {
173 final List<Long> list = new LinkedList<Long>();
174 final long increment = (end - start) / nb;
175
176 /* Wait until the history building completed */
177 if (!stats.waitUntilBuilt()) {
178 return null;
179 }
180
181 /*
182 * We will do one state system query per "border", and save the
183 * differences between each border.
184 */
185 long prevTotal = (start == stats.getStartTime()) ? 0 : getEventCountAt(start);
186 long curTime = start + increment;
187
188 long curTotal, count;
189 for (int i = 0; i < nb - 1; i++) {
190 curTotal = getEventCountAt(curTime);
191 count = curTotal - prevTotal;
192 list.add(count);
193
194 curTime += increment;
195 prevTotal = curTotal;
196 }
197
198 /*
199 * For the last bucket, we'll stretch its end time to the end time of
200 * the requested range, in case it got truncated down.
201 */
202 curTotal = getEventCountAt(end);
203 count = curTotal - prevTotal;
204 list.add(count);
205
206 return list;
207 }
208
209 @Override
210 public long getEventsTotal() {
211 /* We need the complete state history to be built to answer this. */
212 stats.waitUntilBuilt();
213
214 long endTime = stats.getCurrentEndTime();
215 int count = 0;
216
217 try {
218 final int quark = stats.getQuarkAbsolute(Attributes.TOTAL);
219 count= stats.querySingleState(endTime, quark).getStateValue().unboxInt();
220
221 } catch (TimeRangeException e) {
222 /* Assume there is no events for that range */
223 return 0;
224 } catch (AttributeNotFoundException e) {
225 e.printStackTrace();
226 } catch (StateValueTypeException e) {
227 e.printStackTrace();
228 } catch (StateSystemDisposedException e) {
229 e.printStackTrace();
230 }
231
232 return count;
233 }
234
235 @Override
236 public Map<String, Long> getEventTypesTotal() {
237 /* We need the complete state history to be built to answer this. */
238 stats.waitUntilBuilt();
239
240 Map<String, Long> map = new HashMap<String, Long>();
241 long endTime = stats.getCurrentEndTime();
242
243 try {
244 /* Get the list of quarks, one for each even type in the database */
245 int quark = stats.getQuarkAbsolute(Attributes.EVENT_TYPES);
246 List<Integer> quarks = stats.getSubAttributes(quark, false);
247
248 /* Since we want the total we can look only at the end */
249 List<ITmfStateInterval> endState = stats.queryFullState(endTime);
250
251 String curEventName;
252 long eventCount;
253 for (int typeQuark : quarks) {
254 curEventName = stats.getAttributeName(typeQuark);
255 eventCount = endState.get(typeQuark).getStateValue().unboxInt();
256 map.put(curEventName, eventCount);
257 }
258
259 } catch (TimeRangeException e) {
260 /* Assume there is no events, nothing will be put in the map. */
261 } catch (AttributeNotFoundException e) {
262 e.printStackTrace();
263 } catch (StateValueTypeException e) {
264 e.printStackTrace();
265 } catch (StateSystemDisposedException e) {
266 e.printStackTrace();
267 }
268 return map;
269 }
270
271 @Override
272 public long getEventsInRange(long start, long end) {
273 // FIXME Instead of waiting until the end, we could check the current
274 // end time, and answer as soon as possible...
275 stats.waitUntilBuilt();
276
277 long startCount;
278 if (start == stats.getStartTime()) {
279 startCount = 0;
280 } else {
281 /*
282 * We want the events happening at "start" to be included, so we'll
283 * need to query one unit before that point.
284 */
285 startCount = getEventCountAt(start - 1);
286 }
287 long endCount = getEventCountAt(end);
288
289 return endCount - startCount;
290 }
291
292 @Override
293 public Map<String, Long> getEventTypesInRange(long start, long end) {
294 // FIXME Instead of waiting until the end, we could check the current
295 // end time, and answer as soon as possible...
296 stats.waitUntilBuilt();
297
298 Map<String, Long> map = new HashMap<String, Long>();
299
300 /* Make sure the start/end times are within the state history, so we
301 * don't get TimeRange exceptions.
302 */
303 long startTime = checkStartTime(start);
304 long endTime = checkEndTime(end);
305
306 try {
307 /* Get the list of quarks, one for each even type in the database */
308 int quark = stats.getQuarkAbsolute(Attributes.EVENT_TYPES);
309 List<Integer> quarks = stats.getSubAttributes(quark, false);
310
311 List<ITmfStateInterval> endState = stats.queryFullState(endTime);
312
313 String curEventName;
314 long countAtStart, countAtEnd, eventCount;
315
316 if (startTime == stats.getStartTime()) {
317 /* Only use the values picked up at the end time */
318 for (int typeQuark : quarks) {
319 curEventName = stats.getAttributeName(typeQuark);
320 eventCount = endState.get(typeQuark).getStateValue().unboxInt();
321 if (eventCount == -1) {
322 eventCount = 0;
323 }
324 map.put(curEventName, eventCount);
325 }
326 } else {
327 /*
328 * Query the start time at -1, so the beginning of the interval
329 * is inclusive.
330 */
331 List<ITmfStateInterval> startState = stats.queryFullState(startTime - 1);
332 for (int typeQuark : quarks) {
333 curEventName = stats.getAttributeName(typeQuark);
334 countAtStart = startState.get(typeQuark).getStateValue().unboxInt();
335 countAtEnd = endState.get(typeQuark).getStateValue().unboxInt();
336
337 if (countAtStart == -1) {
338 countAtStart = 0;
339 }
340 if (countAtEnd == -1) {
341 countAtEnd = 0;
342 }
343 eventCount = countAtEnd - countAtStart;
344 map.put(curEventName, eventCount);
345 }
346 }
347
348 } catch (TimeRangeException e) {
349 /* Assume there is no events, nothing will be put in the map. */
350 } catch (AttributeNotFoundException e) {
351 /*
352 * These other exception types would show a logic problem however,
353 * so they should not happen.
354 */
355 e.printStackTrace();
356 } catch (StateValueTypeException e) {
357 e.printStackTrace();
358 } catch (StateSystemDisposedException e) {
359 e.printStackTrace();
360 }
361 return map;
362 }
363
364 private long getEventCountAt(long timestamp) {
365 /* Make sure the target time is within the range of the history */
366 long ts = checkStartTime(timestamp);
367 ts = checkEndTime(ts);
368
369 try {
370 final int quark = stats.getQuarkAbsolute(Attributes.TOTAL);
371 long count = stats.querySingleState(ts, quark).getStateValue().unboxInt();
372 return count;
373
374 } catch (TimeRangeException e) {
375 /* Assume there is no events for that range */
376 } catch (AttributeNotFoundException e) {
377 e.printStackTrace();
378 } catch (StateValueTypeException e) {
379 e.printStackTrace();
380 } catch (StateSystemDisposedException e) {
381 e.printStackTrace();
382 }
383
384 return 0;
385 }
386
387 private long checkStartTime(long initialStart) {
388 long start = initialStart;
389 if (start < stats.getStartTime()) {
390 return stats.getStartTime();
391 }
392 return start;
393 }
394
395 private long checkEndTime(long initialEnd) {
396 long end = initialEnd;
397 if (end > stats.getCurrentEndTime()) {
398 return stats.getCurrentEndTime();
399 }
400 return end;
401 }
402
403
404 /**
405 * The attribute names that are used in the state provider
406 */
407 public static class Attributes {
408
409 /** Total nb of events */
410 public static final String TOTAL = "total"; //$NON-NLS-1$
411
412 /** event_types */
413 public static final String EVENT_TYPES = "event_types"; //$NON-NLS-1$<
414 }
415 }
This page took 0.040087 seconds and 6 git commands to generate.