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