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
.signal
.TmfSignalHandler
;
43 import org
.eclipse
.tracecompass
.tmf
.core
.signal
.TmfTraceRangeUpdatedSignal
;
44 import org
.eclipse
.tracecompass
.tmf
.core
.timestamp
.TmfTimeRange
;
45 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.ITmfTrace
;
46 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.ITmfTraceCompleteness
;
47 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.TmfTraceManager
;
48 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.TmfTraceUtils
;
49 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.experiment
.TmfExperiment
;
52 * Abstract analysis module to generate a state system. It is a base class that
53 * can be used as a shortcut by analysis who just need to build a single state
54 * system with a state provider.
56 * Analysis implementing this class should only need to provide a state system
57 * and optionally a backend (default to NULL) and, if required, a filename
58 * (defaults to the analysis'ID)
60 * @author Geneviève Bastien
64 public abstract class TmfStateSystemAnalysisModule
extends TmfAbstractAnalysisModule
65 implements ITmfAnalysisModuleWithStateSystems
{
67 private static final String EXTENSION
= ".ht"; //$NON-NLS-1$
69 private final CountDownLatch fInitialized
= new CountDownLatch(1);
70 private final Object fRequestSyncObj
= new Object();
72 @Nullable private ITmfStateSystemBuilder fStateSystem
;
73 @Nullable private ITmfStateProvider fStateProvider
;
74 @Nullable private IStateHistoryBackend fHtBackend
;
75 @Nullable private ITmfEventRequest fRequest
;
76 @Nullable private TmfTimeRange fTimeRange
= null;
78 private int fNbRead
= 0;
81 * State system backend types
83 * @author Geneviève Bastien
85 protected enum StateSystemBackendType
{
86 /** Full history in file */
88 /** In memory state system */
92 /** State system backed with partial history */
97 * Retrieve a state system belonging to trace, by passing the ID of the
98 * relevant analysis module.
100 * This will start the execution of the analysis module, and start the
101 * construction of the state system, if needed.
104 * The trace for which you want the state system
106 * The ID of the state system analysis module
107 * @return The state system, or null if there was no match
110 public static @Nullable ITmfStateSystem
getStateSystem(ITmfTrace trace
, String moduleId
) {
111 TmfStateSystemAnalysisModule module
=
112 TmfTraceUtils
.getAnalysisModuleOfClass(trace
, TmfStateSystemAnalysisModule
.class, moduleId
);
113 if (module
!= null) {
114 IStatus status
= module
.schedule();
116 module
.waitForInitialization();
117 return module
.getStateSystem();
124 * Retrieve a state system belonging to trace, by passing the class of the
125 * relevant analysis module. If many modules of the same class exists, the
126 * state system of the first one will be returned.
128 * This will start the execution of the analysis module, and start the
129 * construction of the state system, if needed.
132 * The trace for which you want the state system
134 * The class of the state system module to retrieve
135 * @return The state system, or null if there was no match
137 public static @Nullable ITmfStateSystem
getStateSystemByModuleClass(ITmfTrace trace
, Class
<?
extends TmfStateSystemAnalysisModule
> clazz
) {
138 TmfStateSystemAnalysisModule module
= null;
139 for (TmfStateSystemAnalysisModule mod
: TmfTraceUtils
.getAnalysisModulesOfClass(trace
, clazz
)) {
143 if (module
!= null) {
144 IStatus status
= module
.schedule();
146 module
.waitForInitialization();
147 return module
.getStateSystem();
154 * Get the state provider for this analysis module
156 * @return the state provider
158 protected abstract ITmfStateProvider
createStateProvider();
161 * Get the state system backend type used by this module
163 * @return The {@link StateSystemBackendType}
165 protected StateSystemBackendType
getBackendType() {
166 /* Using full history by default, sub-classes can override */
167 return StateSystemBackendType
.FULL
;
171 * Get the supplementary file name where to save this state system. The
172 * default is the ID of the analysis followed by the extension.
174 * @return The supplementary file name
176 protected String
getSsFileName() {
177 return getId() + EXTENSION
;
181 * Get the state system generated by this analysis, or null if it is not yet
184 * @return The state system
187 public ITmfStateSystem
getStateSystem() {
192 * Block the calling thread until the analysis module has been initialized.
193 * After this method returns, {@link #getStateSystem()} should not return
196 public void waitForInitialization() {
198 fInitialized
.await();
199 } catch (InterruptedException e
) {}
202 // ------------------------------------------------------------------------
203 // TmfAbstractAnalysisModule
204 // ------------------------------------------------------------------------
207 protected boolean executeAnalysis(@Nullable final IProgressMonitor monitor
) {
208 IProgressMonitor mon
= (monitor
== null ?
new NullProgressMonitor() : monitor
);
209 final ITmfStateProvider provider
= createStateProvider();
213 /* FIXME: State systems should make use of the monitor, to be cancelled */
215 /* Get the state system according to backend */
216 StateSystemBackendType backend
= getBackendType();
220 ITmfTrace trace
= getTrace();
222 // Analysis was cancelled in the meantime
223 fInitialized
.countDown();
228 directory
= TmfTraceManager
.getSupplementaryFileDir(trace
);
229 htFile
= new File(directory
+ getSsFileName());
230 createFullHistory(id
, provider
, htFile
);
233 directory
= TmfTraceManager
.getSupplementaryFileDir(trace
);
234 htFile
= new File(directory
+ getSsFileName());
235 createPartialHistory(id
, provider
, htFile
);
238 createInMemoryHistory(id
, provider
);
241 createNullHistory(id
, provider
);
246 } catch (TmfTraceException e
) {
247 fInitialized
.countDown();
250 return !mon
.isCanceled();
254 protected void canceling() {
255 ITmfEventRequest req
= fRequest
;
256 if ((req
!= null) && (!req
.isCompleted())) {
262 public void dispose() {
264 if (fStateSystem
!= null) {
265 fStateSystem
.dispose();
269 // ------------------------------------------------------------------------
270 // History creation methods
271 // ------------------------------------------------------------------------
274 * Load the history file matching the target trace. If the file already
275 * exists, it will be opened directly. If not, it will be created from
278 private void createFullHistory(String id
, ITmfStateProvider provider
, File htFile
) throws TmfTraceException
{
280 /* If the target file already exists, do not rebuild it uselessly */
281 // TODO for now we assume it's complete. Might be a good idea to check
282 // at least if its range matches the trace's range.
284 if (htFile
.exists()) {
285 /* Load an existing history */
286 final int version
= provider
.getVersion();
288 IStateHistoryBackend backend
= new HistoryTreeBackend(htFile
, version
);
289 fHtBackend
= backend
;
290 fStateSystem
= StateSystemFactory
.newStateSystem(id
, backend
, false);
291 fInitialized
.countDown();
293 } catch (IOException e
) {
295 * There was an error opening the existing file. Perhaps it was
296 * corrupted, perhaps it's an old version? We'll just
297 * fall-through and try to build a new one from scratch instead.
302 /* Size of the blocking queue to use when building a state history */
303 final int QUEUE_SIZE
= 10000;
306 IStateHistoryBackend backend
= new ThreadedHistoryTreeBackend(htFile
,
307 provider
.getStartTime(), provider
.getVersion(), QUEUE_SIZE
);
308 fHtBackend
= backend
;
309 fStateSystem
= StateSystemFactory
.newStateSystem(id
, backend
);
310 provider
.assignTargetStateSystem(fStateSystem
);
312 } catch (IOException e
) {
314 * If it fails here however, it means there was a problem writing to
315 * the disk, so throw a real exception this time.
317 throw new TmfTraceException(e
.toString(), e
);
322 * Create a new state system backed with a partial history. A partial
323 * history is similar to a "full" one (which you get with
324 * {@link #newFullHistory}), except that the file on disk is much smaller,
325 * but queries are a bit slower.
327 * Also note that single-queries are implemented using a full-query
328 * underneath, (which are much slower), so this might not be a good fit for
329 * a use case where you have to do lots of single queries.
331 private void createPartialHistory(String id
, ITmfStateProvider provider
, File htPartialFile
)
332 throws TmfTraceException
{
334 * The order of initializations is very tricky (but very important!)
335 * here. We need to follow this pattern:
336 * (1 is done before the call to this method)
338 * 1- Instantiate realStateProvider
339 * 2- Instantiate realBackend
340 * 3- Instantiate partialBackend, with prereqs:
341 * 3a- Instantiate partialProvider, via realProvider.getNew()
342 * 3b- Instantiate nullBackend (partialSS's backend)
343 * 3c- Instantiate partialSS
344 * 3d- partialProvider.assignSS(partialSS)
345 * 4- Instantiate realSS
346 * 5- partialSS.assignUpstream(realSS)
347 * 6- realProvider.assignSS(realSS)
348 * 7- Call HistoryBuilder(realProvider, realSS, partialBackend) to build the thing.
351 /* Size of the blocking queue to use when building a state history */
352 final int QUEUE_SIZE
= 10000;
354 final long granularity
= 50000;
357 IStateHistoryBackend realBackend
= null;
359 realBackend
= new ThreadedHistoryTreeBackend(htPartialFile
,
360 provider
.getStartTime(), provider
.getVersion(), QUEUE_SIZE
);
361 } catch (IOException e
) {
362 throw new TmfTraceException(e
.toString(), e
);
366 ITmfStateProvider partialProvider
= provider
.getNewInstance();
368 /* 3b-3c, constructor automatically uses a NullBackend */
369 PartialStateSystem pss
= new PartialStateSystem();
372 partialProvider
.assignTargetStateSystem(pss
);
375 IStateHistoryBackend partialBackend
=
376 new PartialHistoryBackend(partialProvider
, pss
, realBackend
, granularity
);
379 @SuppressWarnings("restriction")
380 org
.eclipse
.tracecompass
.internal
.statesystem
.core
.StateSystem realSS
=
381 (org
.eclipse
.tracecompass
.internal
.statesystem
.core
.StateSystem
) StateSystemFactory
.newStateSystem(id
, partialBackend
);
384 pss
.assignUpstream(realSS
);
387 provider
.assignTargetStateSystem(realSS
);
390 fHtBackend
= partialBackend
;
391 fStateSystem
= realSS
;
397 * Create a new state system using a null history back-end. This means that
398 * no history intervals will be saved anywhere, and as such only
399 * {@link ITmfStateSystem#queryOngoingState} will be available.
401 private void createNullHistory(String id
, ITmfStateProvider provider
) {
402 IStateHistoryBackend backend
= new NullBackend();
403 fHtBackend
= backend
;
404 fStateSystem
= StateSystemFactory
.newStateSystem(id
, backend
);
405 provider
.assignTargetStateSystem(fStateSystem
);
410 * Create a new state system using in-memory interval storage. This should
411 * only be done for very small state system, and will be naturally limited
414 private void createInMemoryHistory(String id
, ITmfStateProvider provider
) {
415 IStateHistoryBackend backend
= new InMemoryBackend(provider
.getStartTime());
416 fHtBackend
= backend
;
417 fStateSystem
= StateSystemFactory
.newStateSystem(id
, backend
);
418 provider
.assignTargetStateSystem(fStateSystem
);
422 private void disposeProvider(boolean deleteFiles
) {
423 ITmfStateProvider provider
= fStateProvider
;
424 if (provider
!= null) {
427 if (deleteFiles
&& (fHtBackend
!= null)) {
428 fHtBackend
.removeFiles();
432 private void build(ITmfStateProvider provider
) {
433 if ((fStateSystem
== null) || (fHtBackend
== null)) {
434 throw new IllegalArgumentException();
437 ITmfEventRequest request
= fRequest
;
438 if ((request
!= null) && (!request
.isCompleted())) {
442 fTimeRange
= TmfTimeRange
.ETERNITY
;
443 final ITmfTrace trace
= provider
.getTrace();
444 if (trace
!= null && !isCompleteTrace(trace
)) {
445 TmfTimeRange traceTimeRange
= trace
.getTimeRange();
446 if (traceTimeRange
!= null) {
447 fTimeRange
= traceTimeRange
;
451 fStateProvider
= provider
;
452 synchronized (fRequestSyncObj
) {
457 * The state system object is now created, we can consider this module
458 * "initialized" (components can retrieve it and start doing queries).
460 fInitialized
.countDown();
463 * Block the executeAnalysis() construction is complete (so that the
464 * progress monitor displays that it is running).
467 if (fRequest
!= null) {
468 fRequest
.waitForCompletion();
470 } catch (InterruptedException e
) {
475 private class StateSystemEventRequest
extends TmfEventRequest
{
476 private final ITmfStateProvider sci
;
477 private final ITmfTrace trace
;
479 public StateSystemEventRequest(ITmfStateProvider sp
, TmfTimeRange timeRange
, int index
) {
480 super(sp
.getExpectedEventType(),
483 ITmfEventRequest
.ALL_DATA
,
484 ITmfEventRequest
.ExecutionType
.BACKGROUND
);
487 // sci.getTrace() will eventually return a @NonNull
488 @SuppressWarnings("null")
489 @NonNull ITmfTrace tr
= sci
.getTrace();
495 public void handleData(final ITmfEvent event
) {
496 super.handleData(event
);
497 if (event
.getTrace() == trace
) {
498 sci
.processEvent(event
);
499 } else if (trace
instanceof TmfExperiment
) {
501 * If the request is for an experiment, check if the event is
502 * from one of the child trace
504 for (ITmfTrace childTrace
: ((TmfExperiment
) trace
).getTraces()) {
505 if (childTrace
== event
.getTrace()) {
506 sci
.processEvent(event
);
513 public void handleSuccess() {
514 super.handleSuccess();
515 if (isCompleteTrace(trace
)) {
516 disposeProvider(false);
518 fNbRead
+= getNbRead();
519 synchronized (fRequestSyncObj
) {
520 final TmfTimeRange timeRange
= fTimeRange
;
521 if (timeRange
!= null) {
522 if (getRange().getEndTime().getValue() < timeRange
.getEndTime().getValue()) {
531 public void handleCancel() {
532 super.handleCancel();
533 if (isCompleteTrace(trace
)) {
534 disposeProvider(true);
539 public void handleFailure() {
540 super.handleFailure();
541 disposeProvider(true);
545 // ------------------------------------------------------------------------
546 // ITmfAnalysisModuleWithStateSystems
547 // ------------------------------------------------------------------------
551 public ITmfStateSystem
getStateSystem(String id
) {
552 if (id
.equals(getId())) {
559 public Iterable
<ITmfStateSystem
> getStateSystems() {
560 @SuppressWarnings("null")
561 @NonNull Iterable
<ITmfStateSystem
> ret
= Collections
.singleton((ITmfStateSystem
) fStateSystem
);
566 * Signal handler for the TmfTraceRangeUpdatedSignal signal
568 * @param signal The incoming signal
571 public void traceRangeUpdated(final TmfTraceRangeUpdatedSignal signal
) {
572 fTimeRange
= signal
.getRange();
573 ITmfStateProvider stateProvider
= fStateProvider
;
574 synchronized (fRequestSyncObj
) {
575 if (signal
.getTrace() == getTrace() && stateProvider
!= null && stateProvider
.getAssignedStateSystem() != null) {
576 ITmfEventRequest request
= fRequest
;
577 if ((request
== null) || request
.isCompleted()) {
584 private void startRequest() {
585 ITmfStateProvider stateProvider
= fStateProvider
;
586 TmfTimeRange timeRange
= fTimeRange
;
587 if (stateProvider
== null || timeRange
== null) {
590 ITmfEventRequest request
= new StateSystemEventRequest(stateProvider
, timeRange
, fNbRead
);
591 stateProvider
.getTrace().sendRequest(request
);
595 private static boolean isCompleteTrace(ITmfTrace trace
) {
596 return !(trace
instanceof ITmfTraceCompleteness
) || ((ITmfTraceCompleteness
) trace
).isComplete();