1 /*******************************************************************************
2 * Copyright (c) 2013, 2014 École Polytechnique de Montréal
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
10 * Geneviève Bastien - Initial API and implementation
11 * Bernd Hufmann - Integrated history builder functionality
12 *******************************************************************************/
14 package org
.eclipse
.tracecompass
.tmf
.core
.statesystem
;
17 import java
.io
.IOException
;
18 import java
.util
.Collections
;
19 import java
.util
.concurrent
.CountDownLatch
;
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
;
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.
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)
56 * @author Geneviève Bastien
60 public abstract class TmfStateSystemAnalysisModule
extends TmfAbstractAnalysisModule
61 implements ITmfAnalysisModuleWithStateSystems
{
63 private static final String EXTENSION
= ".ht"; //$NON-NLS-1$
65 private final CountDownLatch fInitialized
= new CountDownLatch(1);
67 @Nullable private ITmfStateSystemBuilder fStateSystem
;
68 @Nullable private ITmfStateProvider fStateProvider
;
69 @Nullable private IStateHistoryBackend fHtBackend
;
70 @Nullable private ITmfEventRequest fRequest
;
73 * State system backend types
75 * @author Geneviève Bastien
77 protected enum StateSystemBackendType
{
78 /** Full history in file */
80 /** In memory state system */
84 /** State system backed with partial history */
90 * Retrieve a state system belonging to trace, by passing the ID of the
91 * relevant analysis module.
93 * This will start the execution of the analysis module, and start the
94 * construction of the state system, if needed.
97 * The trace for which you want the state system
99 * The ID of the state system analysis module
100 * @return The state system, or null if there was no match
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();
109 module
.waitForInitialization();
110 return module
.getStateSystem();
117 * Get the state provider for this analysis module
119 * @return the state provider
121 protected abstract ITmfStateProvider
createStateProvider();
124 * Get the state system backend type used by this module
126 * @return The {@link StateSystemBackendType}
128 protected StateSystemBackendType
getBackendType() {
129 /* Using full history by default, sub-classes can override */
130 return StateSystemBackendType
.FULL
;
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.
137 * @return The supplementary file name
139 protected String
getSsFileName() {
140 return getId() + EXTENSION
;
144 * Get the state system generated by this analysis, or null if it is not yet
147 * @return The state system
150 public ITmfStateSystem
getStateSystem() {
155 * Block the calling thread until the analysis module has been initialized.
156 * After this method returns, {@link #getStateSystem()} should not return
159 public void waitForInitialization() {
161 fInitialized
.await();
162 } catch (InterruptedException e
) {}
165 // ------------------------------------------------------------------------
166 // TmfAbstractAnalysisModule
167 // ------------------------------------------------------------------------
170 protected boolean executeAnalysis(@Nullable final IProgressMonitor monitor
) {
171 IProgressMonitor mon
= (monitor
== null ?
new NullProgressMonitor() : monitor
);
172 final ITmfStateProvider provider
= createStateProvider();
176 /* FIXME: State systems should make use of the monitor, to be cancelled */
178 /* Get the state system according to backend */
179 StateSystemBackendType backend
= getBackendType();
183 ITmfTrace trace
= getTrace();
185 // Analysis was cancelled in the meantime
186 fInitialized
.countDown();
191 directory
= TmfTraceManager
.getSupplementaryFileDir(trace
);
192 htFile
= new File(directory
+ getSsFileName());
193 createFullHistory(id
, provider
, htFile
);
196 directory
= TmfTraceManager
.getSupplementaryFileDir(trace
);
197 htFile
= new File(directory
+ getSsFileName());
198 createPartialHistory(id
, provider
, htFile
);
201 createInMemoryHistory(id
, provider
);
204 createNullHistory(id
, provider
);
209 } catch (TmfTraceException e
) {
210 fInitialized
.countDown();
213 return !mon
.isCanceled();
217 protected void canceling() {
218 ITmfEventRequest req
= fRequest
;
219 if ((req
!= null) && (!req
.isCompleted())) {
225 public void dispose() {
227 if (fStateSystem
!= null) {
228 fStateSystem
.dispose();
232 // ------------------------------------------------------------------------
233 // History creation methods
234 // ------------------------------------------------------------------------
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
241 private void createFullHistory(String id
, ITmfStateProvider provider
, File htFile
) throws TmfTraceException
{
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.
247 if (htFile
.exists()) {
248 /* Load an existing history */
249 final int version
= provider
.getVersion();
251 IStateHistoryBackend backend
= new HistoryTreeBackend(htFile
, version
);
252 fHtBackend
= backend
;
253 fStateSystem
= StateSystemFactory
.newStateSystem(id
, backend
, false);
254 fInitialized
.countDown();
256 } catch (IOException e
) {
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.
265 /* Size of the blocking queue to use when building a state history */
266 final int QUEUE_SIZE
= 10000;
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
);
275 } catch (IOException e
) {
277 * If it fails here however, it means there was a problem writing to
278 * the disk, so throw a real exception this time.
280 throw new TmfTraceException(e
.toString(), e
);
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.
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.
294 private void createPartialHistory(String id
, ITmfStateProvider provider
, File htPartialFile
)
295 throws TmfTraceException
{
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)
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.
314 /* Size of the blocking queue to use when building a state history */
315 final int QUEUE_SIZE
= 10000;
317 final long granularity
= 50000;
320 IStateHistoryBackend realBackend
= null;
322 realBackend
= new ThreadedHistoryTreeBackend(htPartialFile
,
323 provider
.getStartTime(), provider
.getVersion(), QUEUE_SIZE
);
324 } catch (IOException e
) {
325 throw new TmfTraceException(e
.toString(), e
);
329 ITmfStateProvider partialProvider
= provider
.getNewInstance();
331 /* 3b-3c, constructor automatically uses a NullBackend */
332 PartialStateSystem pss
= new PartialStateSystem();
335 partialProvider
.assignTargetStateSystem(pss
);
338 IStateHistoryBackend partialBackend
=
339 new PartialHistoryBackend(partialProvider
, pss
, realBackend
, granularity
);
342 @SuppressWarnings("restriction")
343 org
.eclipse
.tracecompass
.internal
.statesystem
.core
.StateSystem realSS
=
344 (org
.eclipse
.tracecompass
.internal
.statesystem
.core
.StateSystem
) StateSystemFactory
.newStateSystem(id
, partialBackend
);
347 pss
.assignUpstream(realSS
);
350 provider
.assignTargetStateSystem(realSS
);
353 fHtBackend
= partialBackend
;
354 fStateSystem
= realSS
;
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.
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
);
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
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
);
385 private void disposeProvider(boolean deleteFiles
) {
386 ITmfStateProvider provider
= fStateProvider
;
387 if (provider
!= null) {
390 if (deleteFiles
&& (fHtBackend
!= null)) {
391 fHtBackend
.removeFiles();
395 private void build(ITmfStateProvider provider
) {
396 if ((fStateSystem
== null) || (fHtBackend
== null)) {
397 throw new IllegalArgumentException();
400 ITmfEventRequest request
= fRequest
;
401 if ((request
!= null) && (!request
.isCompleted())) {
405 request
= new StateSystemEventRequest(provider
);
406 provider
.getTrace().sendRequest(request
);
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.
412 fStateProvider
= provider
;
416 * The state system object is now created, we can consider this module
417 * "initialized" (components can retrieve it and start doing queries).
419 fInitialized
.countDown();
422 * Block the executeAnalysis() construction is complete (so that the
423 * progress monitor displays that it is running).
426 request
.waitForCompletion();
427 } catch (InterruptedException e
) {
432 private class StateSystemEventRequest
extends TmfEventRequest
{
433 private final ITmfStateProvider sci
;
434 private final ITmfTrace trace
;
436 public StateSystemEventRequest(ITmfStateProvider sp
) {
437 super(sp
.getExpectedEventType(),
438 TmfTimeRange
.ETERNITY
,
440 ITmfEventRequest
.ALL_DATA
,
441 ITmfEventRequest
.ExecutionType
.BACKGROUND
);
444 // sci.getTrace() will eventually return a @NonNull
445 @SuppressWarnings("null")
446 @NonNull ITmfTrace tr
= sci
.getTrace();
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
) {
458 * If the request is for an experiment, check if the event is
459 * from one of the child trace
461 for (ITmfTrace childTrace
: ((TmfExperiment
) trace
).getTraces()) {
462 if (childTrace
== event
.getTrace()) {
463 sci
.processEvent(event
);
470 public void handleSuccess() {
471 super.handleSuccess();
472 disposeProvider(false);
476 public void handleCancel() {
477 super.handleCancel();
478 disposeProvider(true);
482 public void handleFailure() {
483 super.handleFailure();
484 disposeProvider(true);
488 // ------------------------------------------------------------------------
489 // ITmfAnalysisModuleWithStateSystems
490 // ------------------------------------------------------------------------
494 public ITmfStateSystem
getStateSystem(String id
) {
495 if (id
.equals(getId())) {
502 public Iterable
<ITmfStateSystem
> getStateSystems() {
503 @SuppressWarnings("null")
504 @NonNull Iterable
<ITmfStateSystem
> ret
= Collections
.singleton((ITmfStateSystem
) fStateSystem
);