1 /*******************************************************************************
2 * Copyright (c) 2013, 2015 É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
;
16 import static org
.eclipse
.tracecompass
.common
.core
.NonNullUtils
.checkNotNull
;
19 import java
.io
.IOException
;
20 import java
.util
.Collections
;
21 import java
.util
.concurrent
.CountDownLatch
;
23 import org
.eclipse
.core
.runtime
.IProgressMonitor
;
24 import org
.eclipse
.core
.runtime
.IStatus
;
25 import org
.eclipse
.core
.runtime
.NullProgressMonitor
;
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
.StateHistoryBackendFactory
;
34 import org
.eclipse
.tracecompass
.tmf
.core
.analysis
.TmfAbstractAnalysisModule
;
35 import org
.eclipse
.tracecompass
.tmf
.core
.event
.ITmfEvent
;
36 import org
.eclipse
.tracecompass
.tmf
.core
.exceptions
.TmfTraceException
;
37 import org
.eclipse
.tracecompass
.tmf
.core
.request
.ITmfEventRequest
;
38 import org
.eclipse
.tracecompass
.tmf
.core
.request
.TmfEventRequest
;
39 import org
.eclipse
.tracecompass
.tmf
.core
.signal
.TmfSignalHandler
;
40 import org
.eclipse
.tracecompass
.tmf
.core
.signal
.TmfTraceRangeUpdatedSignal
;
41 import org
.eclipse
.tracecompass
.tmf
.core
.timestamp
.TmfTimeRange
;
42 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.ITmfTrace
;
43 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.ITmfTraceCompleteness
;
44 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.TmfTraceManager
;
45 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.TmfTraceUtils
;
46 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.experiment
.TmfExperiment
;
49 * Abstract analysis module to generate a state system. It is a base class that
50 * can be used as a shortcut by analysis who just need to build a single state
51 * system with a state provider.
53 * Analysis implementing this class should only need to provide a state system
54 * and optionally a backend (default to NULL) and, if required, a filename
55 * (defaults to the analysis'ID)
57 * @author Geneviève Bastien
59 public abstract class TmfStateSystemAnalysisModule
extends TmfAbstractAnalysisModule
60 implements ITmfAnalysisModuleWithStateSystems
{
62 private static final String EXTENSION
= ".ht"; //$NON-NLS-1$
64 private final CountDownLatch fInitialized
= new CountDownLatch(1);
65 private final Object fRequestSyncObj
= new Object();
67 @Nullable private ITmfStateSystemBuilder fStateSystem
;
68 @Nullable private ITmfStateProvider fStateProvider
;
69 @Nullable private IStateHistoryBackend fHtBackend
;
70 @Nullable private ITmfEventRequest fRequest
;
71 @Nullable private TmfTimeRange fTimeRange
= null;
73 private int fNbRead
= 0;
76 * State system backend types
78 * @author Geneviève Bastien
80 protected enum StateSystemBackendType
{
81 /** Full history in file */
83 /** In memory state system */
87 /** State system backed with partial history */
92 * Retrieve a state system belonging to trace, by passing the ID of the
93 * relevant analysis module.
95 * This will start the execution of the analysis module, and start the
96 * construction of the state system, if needed.
99 * The trace for which you want the state system
101 * The ID of the state system analysis module
102 * @return The state system, or null if there was no match
104 public static @Nullable ITmfStateSystem
getStateSystem(ITmfTrace trace
, String moduleId
) {
105 TmfStateSystemAnalysisModule module
=
106 TmfTraceUtils
.getAnalysisModuleOfClass(trace
, TmfStateSystemAnalysisModule
.class, moduleId
);
107 if (module
!= null) {
108 ITmfStateSystem ss
= module
.getStateSystem();
112 IStatus status
= module
.schedule();
114 module
.waitForInitialization();
115 return module
.getStateSystem();
122 * Get the state provider for this analysis module
124 * @return the state provider
126 protected abstract ITmfStateProvider
createStateProvider();
129 * Get the state system backend type used by this module
131 * @return The {@link StateSystemBackendType}
133 protected StateSystemBackendType
getBackendType() {
134 /* Using full history by default, sub-classes can override */
135 return StateSystemBackendType
.FULL
;
139 * Get the supplementary file name where to save this state system. The
140 * default is the ID of the analysis followed by the extension.
142 * @return The supplementary file name
144 protected String
getSsFileName() {
145 return getId() + EXTENSION
;
149 * Get the state system generated by this analysis, or null if it is not yet
152 * @return The state system
155 public ITmfStateSystem
getStateSystem() {
160 * Block the calling thread until the analysis module has been initialized.
161 * After this method returns, {@link #getStateSystem()} should not return
164 public void waitForInitialization() {
166 fInitialized
.await();
167 } catch (InterruptedException e
) {}
170 // ------------------------------------------------------------------------
171 // TmfAbstractAnalysisModule
172 // ------------------------------------------------------------------------
175 protected boolean executeAnalysis(@Nullable final IProgressMonitor monitor
) {
176 IProgressMonitor mon
= (monitor
== null ?
new NullProgressMonitor() : monitor
);
177 final ITmfStateProvider provider
= createStateProvider();
181 /* FIXME: State systems should make use of the monitor, to be cancelled */
183 /* Get the state system according to backend */
184 StateSystemBackendType backend
= getBackendType();
188 ITmfTrace trace
= getTrace();
190 // Analysis was cancelled in the meantime
191 fInitialized
.countDown();
196 directory
= TmfTraceManager
.getSupplementaryFileDir(trace
);
197 htFile
= new File(directory
+ getSsFileName());
198 createFullHistory(id
, provider
, htFile
);
201 directory
= TmfTraceManager
.getSupplementaryFileDir(trace
);
202 htFile
= new File(directory
+ getSsFileName());
203 createPartialHistory(id
, provider
, htFile
);
206 createInMemoryHistory(id
, provider
);
209 createNullHistory(id
, provider
);
214 } catch (TmfTraceException e
) {
215 fInitialized
.countDown();
218 return !mon
.isCanceled();
222 protected void canceling() {
223 ITmfEventRequest req
= fRequest
;
224 if ((req
!= null) && (!req
.isCompleted())) {
230 public void dispose() {
232 if (fStateSystem
!= null) {
233 fStateSystem
.dispose();
237 // ------------------------------------------------------------------------
238 // History creation methods
239 // ------------------------------------------------------------------------
242 * Load the history file matching the target trace. If the file already
243 * exists, it will be opened directly. If not, it will be created from
246 private void createFullHistory(String id
, ITmfStateProvider provider
, File htFile
) throws TmfTraceException
{
248 /* If the target file already exists, do not rebuild it uselessly */
249 // TODO for now we assume it's complete. Might be a good idea to check
250 // at least if its range matches the trace's range.
252 if (htFile
.exists()) {
253 /* Load an existing history */
254 final int version
= provider
.getVersion();
256 IStateHistoryBackend backend
= StateHistoryBackendFactory
.createHistoryTreeBackendExistingFile(
257 id
, htFile
, version
);
258 fHtBackend
= backend
;
259 fStateSystem
= StateSystemFactory
.newStateSystem(backend
, false);
260 fInitialized
.countDown();
262 } catch (IOException e
) {
264 * There was an error opening the existing file. Perhaps it was
265 * corrupted, perhaps it's an old version? We'll just
266 * fall-through and try to build a new one from scratch instead.
271 /* Size of the blocking queue to use when building a state history */
272 final int QUEUE_SIZE
= 256;
275 IStateHistoryBackend backend
= StateHistoryBackendFactory
.createHistoryTreeBackendNewFile(
276 id
, htFile
, provider
.getVersion(), provider
.getStartTime(), QUEUE_SIZE
);
277 fHtBackend
= backend
;
278 fStateSystem
= StateSystemFactory
.newStateSystem(backend
);
279 provider
.assignTargetStateSystem(fStateSystem
);
281 } catch (IOException e
) {
283 * If it fails here however, it means there was a problem writing to
284 * the disk, so throw a real exception this time.
286 throw new TmfTraceException(e
.toString(), e
);
291 * Create a new state system backed with a partial history. A partial
292 * history is similar to a "full" one (which you get with
293 * {@link #newFullHistory}), except that the file on disk is much smaller,
294 * but queries are a bit slower.
296 * Also note that single-queries are implemented using a full-query
297 * underneath, (which are much slower), so this might not be a good fit for
298 * a use case where you have to do lots of single queries.
300 private void createPartialHistory(String id
, ITmfStateProvider provider
, File htPartialFile
)
301 throws TmfTraceException
{
303 * The order of initializations is very tricky (but very important!)
304 * here. We need to follow this pattern:
305 * (1 is done before the call to this method)
307 * 1- Instantiate realStateProvider
308 * 2- Instantiate realBackend
309 * 3- Instantiate partialBackend, with prereqs:
310 * 3a- Instantiate partialProvider, via realProvider.getNew()
311 * 3b- Instantiate nullBackend (partialSS's backend)
312 * 3c- Instantiate partialSS
313 * 3d- partialProvider.assignSS(partialSS)
314 * 4- Instantiate realSS
315 * 5- partialSS.assignUpstream(realSS)
316 * 6- realProvider.assignSS(realSS)
317 * 7- Call HistoryBuilder(realProvider, realSS, partialBackend) to build the thing.
320 /* Size of the blocking queue to use when building a state history */
321 final int QUEUE_SIZE
= 10000;
323 final long granularity
= 50000;
326 IStateHistoryBackend realBackend
= null;
328 realBackend
= StateHistoryBackendFactory
.createHistoryTreeBackendNewFile(
329 id
, htPartialFile
, provider
.getVersion(), provider
.getStartTime(), QUEUE_SIZE
);
330 } catch (IOException e
) {
331 throw new TmfTraceException(e
.toString(), e
);
335 ITmfStateProvider partialProvider
= provider
.getNewInstance();
337 /* 3b-3c, constructor automatically uses a NullBackend */
338 PartialStateSystem pss
= new PartialStateSystem();
341 partialProvider
.assignTargetStateSystem(pss
);
344 String partialId
= new String(id
+ ".partial"); //$NON-NLS-1$
345 IStateHistoryBackend partialBackend
=
346 new PartialHistoryBackend(partialId
, partialProvider
, pss
, realBackend
, granularity
);
349 @SuppressWarnings("restriction")
350 org
.eclipse
.tracecompass
.internal
.statesystem
.core
.StateSystem realSS
=
351 (org
.eclipse
.tracecompass
.internal
.statesystem
.core
.StateSystem
) StateSystemFactory
.newStateSystem(partialBackend
);
354 pss
.assignUpstream(realSS
);
357 provider
.assignTargetStateSystem(realSS
);
360 fHtBackend
= partialBackend
;
361 fStateSystem
= realSS
;
367 * Create a new state system using a null history back-end. This means that
368 * no history intervals will be saved anywhere, and as such only
369 * {@link ITmfStateSystem#queryOngoingState} will be available.
371 private void createNullHistory(String id
, ITmfStateProvider provider
) {
372 IStateHistoryBackend backend
= StateHistoryBackendFactory
.createNullBackend(id
);
373 fHtBackend
= backend
;
374 fStateSystem
= StateSystemFactory
.newStateSystem(backend
);
375 provider
.assignTargetStateSystem(fStateSystem
);
380 * Create a new state system using in-memory interval storage. This should
381 * only be done for very small state system, and will be naturally limited
384 private void createInMemoryHistory(String id
, ITmfStateProvider provider
) {
385 IStateHistoryBackend backend
= StateHistoryBackendFactory
.createInMemoryBackend(id
, provider
.getStartTime());
386 fHtBackend
= backend
;
387 fStateSystem
= StateSystemFactory
.newStateSystem(backend
);
388 provider
.assignTargetStateSystem(fStateSystem
);
392 private void disposeProvider(boolean deleteFiles
) {
393 ITmfStateProvider provider
= fStateProvider
;
394 if (provider
!= null) {
397 if (deleteFiles
&& (fHtBackend
!= null)) {
398 fHtBackend
.removeFiles();
402 private void build(ITmfStateProvider provider
) {
403 if ((fStateSystem
== null) || (fHtBackend
== null)) {
404 throw new IllegalArgumentException();
407 ITmfEventRequest request
= fRequest
;
408 if ((request
!= null) && (!request
.isCompleted())) {
412 fTimeRange
= TmfTimeRange
.ETERNITY
;
413 final ITmfTrace trace
= provider
.getTrace();
414 if (!isCompleteTrace(trace
)) {
415 fTimeRange
= trace
.getTimeRange();
418 fStateProvider
= provider
;
419 synchronized (fRequestSyncObj
) {
424 * The state system object is now created, we can consider this module
425 * "initialized" (components can retrieve it and start doing queries).
427 fInitialized
.countDown();
430 * Block the executeAnalysis() construction is complete (so that the
431 * progress monitor displays that it is running).
434 if (fRequest
!= null) {
435 fRequest
.waitForCompletion();
437 } catch (InterruptedException e
) {
442 private class StateSystemEventRequest
extends TmfEventRequest
{
443 private final ITmfStateProvider sci
;
444 private final ITmfTrace trace
;
446 public StateSystemEventRequest(ITmfStateProvider sp
, TmfTimeRange timeRange
, int index
) {
447 super(ITmfEvent
.class,
450 ITmfEventRequest
.ALL_DATA
,
451 ITmfEventRequest
.ExecutionType
.BACKGROUND
);
454 // sci.getTrace() will eventually return a @NonNull
455 trace
= checkNotNull(sci
.getTrace());
460 public void handleData(final ITmfEvent event
) {
461 super.handleData(event
);
462 if (event
.getTrace() == trace
) {
463 sci
.processEvent(event
);
464 } else if (trace
instanceof TmfExperiment
) {
466 * If the request is for an experiment, check if the event is
467 * from one of the child trace
469 for (ITmfTrace childTrace
: ((TmfExperiment
) trace
).getTraces()) {
470 if (childTrace
== event
.getTrace()) {
471 sci
.processEvent(event
);
478 public void handleSuccess() {
479 super.handleSuccess();
480 if (isCompleteTrace(trace
)) {
481 disposeProvider(false);
483 fNbRead
+= getNbRead();
484 synchronized (fRequestSyncObj
) {
485 final TmfTimeRange timeRange
= fTimeRange
;
486 if (timeRange
!= null) {
487 if (getRange().getEndTime().getValue() < timeRange
.getEndTime().getValue()) {
496 public void handleCancel() {
497 super.handleCancel();
498 if (isCompleteTrace(trace
)) {
499 disposeProvider(true);
504 public void handleFailure() {
505 super.handleFailure();
506 disposeProvider(true);
510 // ------------------------------------------------------------------------
511 // ITmfAnalysisModuleWithStateSystems
512 // ------------------------------------------------------------------------
516 public ITmfStateSystem
getStateSystem(String id
) {
517 if (id
.equals(getId())) {
524 public Iterable
<ITmfStateSystem
> getStateSystems() {
525 return checkNotNull(Collections
.<ITmfStateSystem
> singleton(fStateSystem
));
529 * Signal handler for the TmfTraceRangeUpdatedSignal signal
531 * @param signal The incoming signal
534 public void traceRangeUpdated(final TmfTraceRangeUpdatedSignal signal
) {
535 fTimeRange
= signal
.getRange();
536 ITmfStateProvider stateProvider
= fStateProvider
;
537 synchronized (fRequestSyncObj
) {
538 if (signal
.getTrace() == getTrace() && stateProvider
!= null && stateProvider
.getAssignedStateSystem() != null) {
539 ITmfEventRequest request
= fRequest
;
540 if ((request
== null) || request
.isCompleted()) {
547 private void startRequest() {
548 ITmfStateProvider stateProvider
= fStateProvider
;
549 TmfTimeRange timeRange
= fTimeRange
;
550 if (stateProvider
== null || timeRange
== null) {
553 ITmfEventRequest request
= new StateSystemEventRequest(stateProvider
, timeRange
, fNbRead
);
554 stateProvider
.getTrace().sendRequest(request
);
558 private static boolean isCompleteTrace(ITmfTrace trace
) {
559 return !(trace
instanceof ITmfTraceCompleteness
) || ((ITmfTraceCompleteness
) trace
).isComplete();