c85961db81c38fa3289725a519d324f359ee4005
[deliverable/tracecompass.git] / org.eclipse.linuxtools.tmf.core / src / org / eclipse / linuxtools / tmf / core / statesystem / TmfStateSystemAnalysisModule.java
1 /*******************************************************************************
2 * Copyright (c) 2013, 2014 École Polytechnique de Montréal
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 * Geneviève Bastien - Initial API and implementation
11 * Bernd Hufmann - Integrated history builder functionality
12 *******************************************************************************/
13
14 package org.eclipse.linuxtools.tmf.core.statesystem;
15
16 import java.io.File;
17 import java.io.IOException;
18 import java.util.Collections;
19 import java.util.concurrent.CountDownLatch;
20
21 import org.eclipse.core.runtime.IProgressMonitor;
22 import org.eclipse.core.runtime.NullProgressMonitor;
23 import org.eclipse.jdt.annotation.NonNull;
24 import org.eclipse.jdt.annotation.NonNullByDefault;
25 import org.eclipse.jdt.annotation.Nullable;
26 import org.eclipse.linuxtools.internal.tmf.core.statesystem.StateSystem;
27 import org.eclipse.linuxtools.internal.tmf.core.statesystem.backends.IStateHistoryBackend;
28 import org.eclipse.linuxtools.internal.tmf.core.statesystem.backends.InMemoryBackend;
29 import org.eclipse.linuxtools.internal.tmf.core.statesystem.backends.NullBackend;
30 import org.eclipse.linuxtools.internal.tmf.core.statesystem.backends.historytree.HistoryTreeBackend;
31 import org.eclipse.linuxtools.internal.tmf.core.statesystem.backends.historytree.ThreadedHistoryTreeBackend;
32 import org.eclipse.linuxtools.internal.tmf.core.statesystem.backends.partial.PartialHistoryBackend;
33 import org.eclipse.linuxtools.internal.tmf.core.statesystem.backends.partial.PartialStateSystem;
34 import org.eclipse.linuxtools.tmf.core.analysis.TmfAbstractAnalysisModule;
35 import org.eclipse.linuxtools.tmf.core.event.ITmfEvent;
36 import org.eclipse.linuxtools.tmf.core.exceptions.TmfTraceException;
37 import org.eclipse.linuxtools.tmf.core.request.ITmfEventRequest;
38 import org.eclipse.linuxtools.tmf.core.request.TmfEventRequest;
39 import org.eclipse.linuxtools.tmf.core.timestamp.TmfTimeRange;
40 import org.eclipse.linuxtools.tmf.core.trace.ITmfTrace;
41 import org.eclipse.linuxtools.tmf.core.trace.TmfTraceManager;
42
43 /**
44 * Abstract analysis module to generate a state system. It is a base class that
45 * can be used as a shortcut by analysis who just need to build a single state
46 * system with a state provider.
47 *
48 * Analysis implementing this class should only need to provide a state system
49 * and optionally a backend (default to NULL) and, if required, a filename
50 * (defaults to the analysis'ID)
51 *
52 * @author Geneviève Bastien
53 * @since 3.0
54 */
55 @NonNullByDefault
56 public abstract class TmfStateSystemAnalysisModule extends TmfAbstractAnalysisModule
57 implements ITmfAnalysisModuleWithStateSystems {
58
59 private static final String EXTENSION = ".ht"; //$NON-NLS-1$
60
61 private final CountDownLatch fInitialized = new CountDownLatch(1);
62
63 @Nullable private ITmfStateSystemBuilder fStateSystem;
64 @Nullable private ITmfStateProvider fStateProvider;
65 @Nullable private IStateHistoryBackend fHtBackend;
66 @Nullable private ITmfEventRequest fRequest;
67
68 /**
69 * State system backend types
70 *
71 * @author Geneviève Bastien
72 */
73 protected enum StateSystemBackendType {
74 /** Full history in file */
75 FULL,
76 /** In memory state system */
77 INMEM,
78 /** Null history */
79 NULL,
80 /** State system backed with partial history */
81 PARTIAL
82 }
83
84 /**
85 * Get the state provider for this analysis module
86 *
87 * @return the state provider
88 */
89 protected abstract ITmfStateProvider createStateProvider();
90
91 /**
92 * Get the state system backend type used by this module
93 *
94 * @return The {@link StateSystemBackendType}
95 */
96 protected StateSystemBackendType getBackendType() {
97 /* Using full history by default, sub-classes can override */
98 return StateSystemBackendType.FULL;
99 }
100
101 /**
102 * Get the supplementary file name where to save this state system. The
103 * default is the ID of the analysis followed by the extension.
104 *
105 * @return The supplementary file name
106 */
107 protected String getSsFileName() {
108 return getId() + EXTENSION;
109 }
110
111 /**
112 * Get the state system generated by this analysis, or null if it is not yet
113 * created.
114 *
115 * @return The state system
116 */
117 @Nullable
118 public ITmfStateSystem getStateSystem() {
119 return fStateSystem;
120 }
121
122 /**
123 * Block the calling thread until the analysis module has been initialized.
124 * After this method returns, {@link #getStateSystem()} should not return
125 * null anymore.
126 */
127 public void waitForInitialization() {
128 try {
129 fInitialized.await();
130 } catch (InterruptedException e) {}
131 }
132
133 // ------------------------------------------------------------------------
134 // TmfAbstractAnalysisModule
135 // ------------------------------------------------------------------------
136
137 @Override
138 protected boolean executeAnalysis(@Nullable final IProgressMonitor monitor) {
139 IProgressMonitor mon = (monitor == null ? new NullProgressMonitor() : monitor);
140 final ITmfStateProvider provider = createStateProvider();
141
142 String id = getId();
143
144 /* FIXME: State systems should make use of the monitor, to be cancelled */
145 try {
146 /* Get the state system according to backend */
147 StateSystemBackendType backend = getBackendType();
148 String directory;
149 File htFile;
150 switch (backend) {
151 case FULL:
152 directory = TmfTraceManager.getSupplementaryFileDir(getTrace());
153 htFile = new File(directory + getSsFileName());
154 createFullHistory(id, provider, htFile);
155 break;
156 case PARTIAL:
157 directory = TmfTraceManager.getSupplementaryFileDir(getTrace());
158 htFile = new File(directory + getSsFileName());
159 createPartialHistory(id, provider, htFile);
160 break;
161 case INMEM:
162 createInMemoryHistory(id, provider);
163 break;
164 case NULL:
165 createNullHistory(id, provider);
166 break;
167 default:
168 break;
169 }
170 } catch (TmfTraceException e) {
171 return false;
172 }
173 return !mon.isCanceled();
174 }
175
176 @Override
177 protected void canceling() {
178 ITmfEventRequest req = fRequest;
179 if ((req != null) && (!req.isCompleted())) {
180 req.cancel();
181 }
182 }
183
184 @Override
185 public void dispose() {
186 super.dispose();
187 if (fStateSystem != null) {
188 fStateSystem.dispose();
189 }
190 }
191
192 // ------------------------------------------------------------------------
193 // History creation methods
194 // ------------------------------------------------------------------------
195
196 /*
197 * Load the history file matching the target trace. If the file already
198 * exists, it will be opened directly. If not, it will be created from
199 * scratch.
200 */
201 private void createFullHistory(String id, ITmfStateProvider provider, File htFile) throws TmfTraceException {
202
203 /* If the target file already exists, do not rebuild it uselessly */
204 // TODO for now we assume it's complete. Might be a good idea to check
205 // at least if its range matches the trace's range.
206
207 if (htFile.exists()) {
208 /* Load an existing history */
209 final int version = provider.getVersion();
210 try {
211 IStateHistoryBackend backend = new HistoryTreeBackend(htFile, version);
212 fHtBackend = backend;
213 fStateSystem = new StateSystem(id, backend, false);
214 fInitialized.countDown();
215 return;
216 } catch (IOException e) {
217 /*
218 * There was an error opening the existing file. Perhaps it was
219 * corrupted, perhaps it's an old version? We'll just
220 * fall-through and try to build a new one from scratch instead.
221 */
222 }
223 }
224
225 /* Size of the blocking queue to use when building a state history */
226 final int QUEUE_SIZE = 10000;
227
228 try {
229 IStateHistoryBackend backend = new ThreadedHistoryTreeBackend(htFile,
230 provider.getStartTime(), provider.getVersion(), QUEUE_SIZE);
231 fHtBackend = backend;
232 fStateSystem = new StateSystem(id, backend);
233 provider.assignTargetStateSystem(fStateSystem);
234 build(provider);
235 } catch (IOException e) {
236 /*
237 * If it fails here however, it means there was a problem writing to
238 * the disk, so throw a real exception this time.
239 */
240 throw new TmfTraceException(e.toString(), e);
241 }
242 }
243
244 /*
245 * Create a new state system backed with a partial history. A partial
246 * history is similar to a "full" one (which you get with
247 * {@link #newFullHistory}), except that the file on disk is much smaller,
248 * but queries are a bit slower.
249 *
250 * Also note that single-queries are implemented using a full-query
251 * underneath, (which are much slower), so this might not be a good fit for
252 * a use case where you have to do lots of single queries.
253 */
254 private void createPartialHistory(String id, ITmfStateProvider provider, File htPartialFile)
255 throws TmfTraceException {
256 /*
257 * The order of initializations is very tricky (but very important!)
258 * here. We need to follow this pattern:
259 * (1 is done before the call to this method)
260 *
261 * 1- Instantiate realStateProvider
262 * 2- Instantiate realBackend
263 * 3- Instantiate partialBackend, with prereqs:
264 * 3a- Instantiate partialProvider, via realProvider.getNew()
265 * 3b- Instantiate nullBackend (partialSS's backend)
266 * 3c- Instantiate partialSS
267 * 3d- partialProvider.assignSS(partialSS)
268 * 4- Instantiate realSS
269 * 5- partialSS.assignUpstream(realSS)
270 * 6- realProvider.assignSS(realSS)
271 * 7- Call HistoryBuilder(realProvider, realSS, partialBackend) to build the thing.
272 */
273
274 /* Size of the blocking queue to use when building a state history */
275 final int QUEUE_SIZE = 10000;
276
277 final long granularity = 50000;
278
279 /* 2 */
280 IStateHistoryBackend realBackend = null;
281 try {
282 realBackend = new ThreadedHistoryTreeBackend(htPartialFile,
283 provider.getStartTime(), provider.getVersion(), QUEUE_SIZE);
284 } catch (IOException e) {
285 throw new TmfTraceException(e.toString(), e);
286 }
287
288 /* 3a */
289 ITmfStateProvider partialProvider = provider.getNewInstance();
290
291 /* 3b-3c, constructor automatically uses a NullBackend */
292 PartialStateSystem pss = new PartialStateSystem();
293
294 /* 3d */
295 partialProvider.assignTargetStateSystem(pss);
296
297 /* 3 */
298 IStateHistoryBackend partialBackend =
299 new PartialHistoryBackend(partialProvider, pss, realBackend, granularity);
300
301 /* 4 */
302 StateSystem realSS = new StateSystem(id, partialBackend);
303
304 /* 5 */
305 pss.assignUpstream(realSS);
306
307 /* 6 */
308 provider.assignTargetStateSystem(realSS);
309
310 /* 7 */
311 fHtBackend = partialBackend;
312 fStateSystem = realSS;
313
314 build(provider);
315 }
316
317 /*
318 * Create a new state system using a null history back-end. This means that
319 * no history intervals will be saved anywhere, and as such only
320 * {@link ITmfStateSystem#queryOngoingState} will be available.
321 */
322 private void createNullHistory(String id, ITmfStateProvider provider) {
323 IStateHistoryBackend backend = new NullBackend();
324 fHtBackend = backend;
325 fStateSystem = new StateSystem(id, backend);
326 provider.assignTargetStateSystem(fStateSystem);
327 build(provider);
328 }
329
330 /*
331 * Create a new state system using in-memory interval storage. This should
332 * only be done for very small state system, and will be naturally limited
333 * to 2^31 intervals.
334 */
335 private void createInMemoryHistory(String id, ITmfStateProvider provider) {
336 IStateHistoryBackend backend = new InMemoryBackend(provider.getStartTime());
337 fHtBackend = backend;
338 fStateSystem = new StateSystem(id, backend);
339 provider.assignTargetStateSystem(fStateSystem);
340 build(provider);
341 }
342
343 private void disposeProvider(boolean deleteFiles) {
344 ITmfStateProvider provider = fStateProvider;
345 if (provider != null) {
346 provider.dispose();
347 }
348 if (deleteFiles && (fHtBackend != null)) {
349 fHtBackend.removeFiles();
350 }
351 }
352
353 private void build(ITmfStateProvider provider) {
354 if ((fStateSystem == null) || (fHtBackend == null)) {
355 throw new IllegalArgumentException();
356 }
357
358 ITmfEventRequest request = fRequest;
359 if ((request != null) && (!request.isCompleted())) {
360 request.cancel();
361 }
362
363 request = new StateSystemEventRequest(provider);
364 provider.getTrace().sendRequest(request);
365
366 /*
367 * Only now that we've actually started the build, we'll update the
368 * class fields, so that they become visible for other callers.
369 */
370 fStateProvider = provider;
371 fRequest = request;
372
373 /*
374 * The state system object is now created, we can consider this module
375 * "initialized" (components can retrieve it and start doing queries).
376 */
377 fInitialized.countDown();
378
379 /*
380 * Block the executeAnalysis() construction is complete (so that the
381 * progress monitor displays that it is running).
382 */
383 try {
384 request.waitForCompletion();
385 } catch (InterruptedException e) {
386 e.printStackTrace();
387 }
388 }
389
390 private class StateSystemEventRequest extends TmfEventRequest {
391 private final ITmfStateProvider sci;
392 private final ITmfTrace trace;
393
394 public StateSystemEventRequest(ITmfStateProvider sp) {
395 super(sp.getExpectedEventType(),
396 TmfTimeRange.ETERNITY,
397 0,
398 ITmfEventRequest.ALL_DATA,
399 ITmfEventRequest.ExecutionType.BACKGROUND);
400 this.sci = sp;
401
402 // sci.getTrace() will eventually return a @NonNull
403 @SuppressWarnings("null")
404 @NonNull ITmfTrace tr = sci.getTrace();
405
406 this.trace = tr;
407 }
408
409 @Override
410 public void handleData(final @Nullable ITmfEvent event) {
411 super.handleData(event);
412 if (event != null && event.getTrace() == trace) {
413 sci.processEvent(event);
414 }
415 }
416
417 @Override
418 public void handleSuccess() {
419 super.handleSuccess();
420 disposeProvider(false);
421 }
422
423 @Override
424 public void handleCancel() {
425 super.handleCancel();
426 disposeProvider(true);
427 }
428
429 @Override
430 public void handleFailure() {
431 super.handleFailure();
432 disposeProvider(true);
433 }
434 }
435
436 // ------------------------------------------------------------------------
437 // ITmfAnalysisModuleWithStateSystems
438 // ------------------------------------------------------------------------
439
440 @Override
441 @Nullable
442 public ITmfStateSystem getStateSystem(String id) {
443 if (id.equals(getId())) {
444 return fStateSystem;
445 }
446 return null;
447 }
448
449 @Override
450 public Iterable<ITmfStateSystem> getStateSystems() {
451 @SuppressWarnings("null")
452 @NonNull Iterable<ITmfStateSystem> ret = Collections.singleton((ITmfStateSystem) fStateSystem);
453 return ret;
454 }
455 }
This page took 0.040818 seconds and 4 git commands to generate.