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
;
17 import java
.io
.IOException
;
18 import java
.util
.Collections
;
20 import java
.util
.concurrent
.CountDownLatch
;
22 import org
.apache
.commons
.io
.FileUtils
;
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
.NonNull
;
27 import org
.eclipse
.jdt
.annotation
.Nullable
;
28 import org
.eclipse
.tracecompass
.common
.core
.NonNullUtils
;
29 import org
.eclipse
.tracecompass
.internal
.tmf
.core
.statesystem
.backends
.partial
.PartialHistoryBackend
;
30 import org
.eclipse
.tracecompass
.internal
.tmf
.core
.statesystem
.backends
.partial
.PartialStateSystem
;
31 import org
.eclipse
.tracecompass
.statesystem
.core
.ITmfStateSystem
;
32 import org
.eclipse
.tracecompass
.statesystem
.core
.ITmfStateSystemBuilder
;
33 import org
.eclipse
.tracecompass
.statesystem
.core
.StateSystemFactory
;
34 import org
.eclipse
.tracecompass
.statesystem
.core
.backend
.IStateHistoryBackend
;
35 import org
.eclipse
.tracecompass
.statesystem
.core
.backend
.StateHistoryBackendFactory
;
36 import org
.eclipse
.tracecompass
.tmf
.core
.analysis
.TmfAbstractAnalysisModule
;
37 import org
.eclipse
.tracecompass
.tmf
.core
.event
.ITmfEvent
;
38 import org
.eclipse
.tracecompass
.tmf
.core
.exceptions
.TmfTraceException
;
39 import org
.eclipse
.tracecompass
.tmf
.core
.request
.ITmfEventRequest
;
40 import org
.eclipse
.tracecompass
.tmf
.core
.request
.TmfEventRequest
;
41 import org
.eclipse
.tracecompass
.tmf
.core
.signal
.TmfSignalHandler
;
42 import org
.eclipse
.tracecompass
.tmf
.core
.signal
.TmfTraceRangeUpdatedSignal
;
43 import org
.eclipse
.tracecompass
.tmf
.core
.timestamp
.TmfTimeRange
;
44 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.ITmfTrace
;
45 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.ITmfTraceCompleteness
;
46 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.TmfTraceManager
;
47 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.TmfTraceUtils
;
48 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.experiment
.TmfExperiment
;
51 * Abstract analysis module to generate a state system. It is a base class that
52 * can be used as a shortcut by analysis who just need to build a single state
53 * system with a state provider.
55 * Analysis implementing this class should only need to provide a state system
56 * and optionally a backend (default to NULL) and, if required, a filename
57 * (defaults to the analysis'ID)
59 * @author Geneviève Bastien
61 public abstract class TmfStateSystemAnalysisModule
extends TmfAbstractAnalysisModule
62 implements ITmfAnalysisModuleWithStateSystems
{
64 private static final String EXTENSION
= ".ht"; //$NON-NLS-1$
66 private final CountDownLatch fInitialized
= new CountDownLatch(1);
67 private final Object fRequestSyncObj
= new Object();
69 private @Nullable ITmfStateSystemBuilder fStateSystem
;
70 private @Nullable ITmfEventRequest fRequest
;
71 private @Nullable TmfTimeRange fTimeRange
= null;
73 private int fNbRead
= 0;
74 private boolean fInitializationSucceeded
;
76 private volatile @Nullable ITmfStateProvider fStateProvider
;
79 * State system backend types
81 * @author Geneviève Bastien
83 protected enum StateSystemBackendType
{
84 /** Full history in file */
86 /** In memory state system */
90 /** State system backed with partial history */
95 * Retrieve a state system belonging to trace, by passing the ID of the
96 * relevant analysis module.
98 * This will start the execution of the analysis module, and start the
99 * construction of the state system, if needed.
102 * The trace for which you want the state system
104 * The ID of the state system analysis module
105 * @return The state system, or null if there was no match or the module was
106 * not initialized correctly
108 public static @Nullable ITmfStateSystem
getStateSystem(ITmfTrace trace
, String moduleId
) {
109 TmfStateSystemAnalysisModule module
=
110 TmfTraceUtils
.getAnalysisModuleOfClass(trace
, TmfStateSystemAnalysisModule
.class, moduleId
);
111 if (module
!= null) {
112 ITmfStateSystem ss
= module
.getStateSystem();
116 IStatus status
= module
.schedule();
118 return module
.waitForInitialization() ? module
.getStateSystem() : null;
125 * Get the state provider for this analysis module
127 * @return the state provider
129 protected abstract ITmfStateProvider
createStateProvider();
132 * Get the state system backend type used by this module
134 * @return The {@link StateSystemBackendType}
136 protected StateSystemBackendType
getBackendType() {
137 /* Using full history by default, sub-classes can override */
138 return StateSystemBackendType
.FULL
;
142 * Get the supplementary file name where to save this state system. The
143 * default is the ID of the analysis followed by the extension.
145 * @return The supplementary file name
147 protected String
getSsFileName() {
148 return getId() + EXTENSION
;
152 * Get the state system generated by this analysis, or null if it is not yet
155 * @return The state system
158 public ITmfStateSystem
getStateSystem() {
166 public boolean waitForInitialization() {
168 fInitialized
.await();
169 } catch (InterruptedException e
) {
172 return fInitializationSucceeded
;
179 public boolean isQueryable(long ts
) {
180 /* Return true if there is no state provider available (the analysis is not being built) */
181 ITmfStateProvider provider
= fStateProvider
;
182 if (provider
== null) {
185 return ts
<= provider
.getLatestSafeTime();
188 // ------------------------------------------------------------------------
189 // TmfAbstractAnalysisModule
190 // ------------------------------------------------------------------------
192 private @Nullable File
getSsFile() {
193 ITmfTrace trace
= getTrace();
197 String directory
= TmfTraceManager
.getSupplementaryFileDir(trace
);
198 File htFile
= new File(directory
+ getSsFileName());
203 protected boolean executeAnalysis(@Nullable final IProgressMonitor monitor
) {
204 IProgressMonitor mon
= (monitor
== null ?
new NullProgressMonitor() : monitor
);
205 final ITmfStateProvider provider
= createStateProvider();
209 /* FIXME: State systems should make use of the monitor, to be cancelled */
211 /* Get the state system according to backend */
212 StateSystemBackendType backend
= getBackendType();
214 ITmfTrace trace
= getTrace();
216 // Analysis was cancelled in the meantime
217 analysisReady(false);
222 File htFile
= getSsFile();
223 if (htFile
== null) {
226 createFullHistory(id
, provider
, htFile
);
230 File htFile
= getSsFile();
231 if (htFile
== null) {
234 createPartialHistory(id
, provider
, htFile
);
238 createInMemoryHistory(id
, provider
);
241 createNullHistory(id
, provider
);
246 } catch (TmfTraceException e
) {
247 analysisReady(false);
250 return !mon
.isCanceled();
254 * Make the module available and set whether the initialization succeeded or
255 * not. If not, no state system is available and
256 * {@link #waitForInitialization()} should return false.
259 * True if the initialization succeeded, false otherwise
261 private void analysisReady(boolean succeeded
) {
262 fInitializationSucceeded
= succeeded
;
263 fInitialized
.countDown();
267 protected void canceling() {
268 ITmfEventRequest req
= fRequest
;
269 if ((req
!= null) && (!req
.isCompleted())) {
275 public void dispose() {
277 if (fStateSystem
!= null) {
278 fStateSystem
.dispose();
282 // ------------------------------------------------------------------------
283 // History creation methods
284 // ------------------------------------------------------------------------
287 * Load the history file matching the target trace. If the file already
288 * exists, it will be opened directly. If not, it will be created from
291 private void createFullHistory(String id
, ITmfStateProvider provider
, File htFile
) throws TmfTraceException
{
293 /* If the target file already exists, do not rebuild it uselessly */
294 // TODO for now we assume it's complete. Might be a good idea to check
295 // at least if its range matches the trace's range.
297 if (htFile
.exists()) {
298 /* Load an existing history */
299 final int version
= provider
.getVersion();
301 IStateHistoryBackend backend
= StateHistoryBackendFactory
.createHistoryTreeBackendExistingFile(
302 id
, htFile
, version
);
303 fStateSystem
= StateSystemFactory
.newStateSystem(backend
, false);
306 } catch (IOException e
) {
308 * There was an error opening the existing file. Perhaps it was
309 * corrupted, perhaps it's an old version? We'll just
310 * fall-through and try to build a new one from scratch instead.
315 /* Size of the blocking queue to use when building a state history */
316 final int QUEUE_SIZE
= 10000;
319 IStateHistoryBackend backend
= StateHistoryBackendFactory
.createHistoryTreeBackendNewFile(
320 id
, htFile
, provider
.getVersion(), provider
.getStartTime(), QUEUE_SIZE
);
321 fStateSystem
= StateSystemFactory
.newStateSystem(backend
);
322 provider
.assignTargetStateSystem(fStateSystem
);
324 } catch (IOException e
) {
326 * If it fails here however, it means there was a problem writing to
327 * the disk, so throw a real exception this time.
329 throw new TmfTraceException(e
.toString(), e
);
334 * Create a new state system backed with a partial history. A partial
335 * history is similar to a "full" one (which you get with
336 * {@link #newFullHistory}), except that the file on disk is much smaller,
337 * but queries are a bit slower.
339 * Also note that single-queries are implemented using a full-query
340 * underneath, (which are much slower), so this might not be a good fit for
341 * a use case where you have to do lots of single queries.
343 private void createPartialHistory(String id
, ITmfStateProvider provider
, File htPartialFile
)
344 throws TmfTraceException
{
346 * The order of initializations is very tricky (but very important!)
347 * here. We need to follow this pattern:
348 * (1 is done before the call to this method)
350 * 1- Instantiate realStateProvider
351 * 2- Instantiate realBackend
352 * 3- Instantiate partialBackend, with prereqs:
353 * 3a- Instantiate partialProvider, via realProvider.getNew()
354 * 3b- Instantiate nullBackend (partialSS's backend)
355 * 3c- Instantiate partialSS
356 * 3d- partialProvider.assignSS(partialSS)
357 * 4- Instantiate realSS
358 * 5- partialSS.assignUpstream(realSS)
359 * 6- realProvider.assignSS(realSS)
360 * 7- Call HistoryBuilder(realProvider, realSS, partialBackend) to build the thing.
363 /* Size of the blocking queue to use when building a state history */
364 final int QUEUE_SIZE
= 10000;
366 final long granularity
= 50000;
369 IStateHistoryBackend realBackend
= null;
371 realBackend
= StateHistoryBackendFactory
.createHistoryTreeBackendNewFile(
372 id
, htPartialFile
, provider
.getVersion(), provider
.getStartTime(), QUEUE_SIZE
);
373 } catch (IOException e
) {
374 throw new TmfTraceException(e
.toString(), e
);
378 ITmfStateProvider partialProvider
= provider
.getNewInstance();
380 /* 3b-3c, constructor automatically uses a NullBackend */
381 PartialStateSystem pss
= new PartialStateSystem();
384 partialProvider
.assignTargetStateSystem(pss
);
387 IStateHistoryBackend partialBackend
= new PartialHistoryBackend(id
+ ".partial", partialProvider
, pss
, realBackend
, granularity
); //$NON-NLS-1$
390 @SuppressWarnings("restriction")
391 org
.eclipse
.tracecompass
.internal
.statesystem
.core
.StateSystem realSS
=
392 (org
.eclipse
.tracecompass
.internal
.statesystem
.core
.StateSystem
) StateSystemFactory
.newStateSystem(partialBackend
);
395 pss
.assignUpstream(realSS
);
398 provider
.assignTargetStateSystem(realSS
);
401 fStateSystem
= realSS
;
407 * Create a new state system using a null history back-end. This means that
408 * no history intervals will be saved anywhere, and as such only
409 * {@link ITmfStateSystem#queryOngoingState} will be available.
411 private void createNullHistory(String id
, ITmfStateProvider provider
) {
412 IStateHistoryBackend backend
= StateHistoryBackendFactory
.createNullBackend(id
);
413 fStateSystem
= StateSystemFactory
.newStateSystem(backend
);
414 provider
.assignTargetStateSystem(fStateSystem
);
419 * Create a new state system using in-memory interval storage. This should
420 * only be done for very small state system, and will be naturally limited
423 private void createInMemoryHistory(String id
, ITmfStateProvider provider
) {
424 IStateHistoryBackend backend
= StateHistoryBackendFactory
.createInMemoryBackend(id
, provider
.getStartTime());
425 fStateSystem
= StateSystemFactory
.newStateSystem(backend
);
426 provider
.assignTargetStateSystem(fStateSystem
);
430 private void disposeProvider(boolean deleteFiles
) {
431 ITmfStateProvider provider
= fStateProvider
;
432 if (provider
!= null) {
435 fStateProvider
= null;
436 if (deleteFiles
&& (fStateSystem
!= null)) {
437 fStateSystem
.removeFiles();
441 private void build(ITmfStateProvider provider
) {
442 if (fStateSystem
== null) {
443 throw new IllegalArgumentException();
446 ITmfEventRequest request
= fRequest
;
447 if ((request
!= null) && (!request
.isCompleted())) {
451 fTimeRange
= TmfTimeRange
.ETERNITY
;
452 final ITmfTrace trace
= provider
.getTrace();
453 if (!isCompleteTrace(trace
)) {
454 fTimeRange
= trace
.getTimeRange();
457 fStateProvider
= provider
;
458 synchronized (fRequestSyncObj
) {
463 * The state system object is now created, we can consider this module
464 * "initialized" (components can retrieve it and start doing queries).
469 * Block the executeAnalysis() construction is complete (so that the
470 * progress monitor displays that it is running).
473 if (fRequest
!= null) {
474 fRequest
.waitForCompletion();
476 } catch (InterruptedException e
) {
481 private class StateSystemEventRequest
extends TmfEventRequest
{
482 private final ITmfStateProvider sci
;
483 private final ITmfTrace trace
;
485 public StateSystemEventRequest(ITmfStateProvider sp
, TmfTimeRange timeRange
, int index
) {
486 super(ITmfEvent
.class,
489 ITmfEventRequest
.ALL_DATA
,
490 ITmfEventRequest
.ExecutionType
.BACKGROUND
,
491 TmfStateSystemAnalysisModule
.this.getDependencyLevel());
493 trace
= sci
.getTrace();
498 public void handleData(final ITmfEvent event
) {
499 super.handleData(event
);
500 if (event
.getTrace() == trace
) {
501 sci
.processEvent(event
);
502 } else if (trace
instanceof TmfExperiment
) {
504 * If the request is for an experiment, check if the event is
505 * from one of the child trace
507 for (ITmfTrace childTrace
: ((TmfExperiment
) trace
).getTraces()) {
508 if (childTrace
== event
.getTrace()) {
509 sci
.processEvent(event
);
516 public void handleSuccess() {
517 super.handleSuccess();
518 if (isCompleteTrace(trace
)) {
519 disposeProvider(false);
521 fNbRead
+= getNbRead();
522 synchronized (fRequestSyncObj
) {
523 final TmfTimeRange timeRange
= fTimeRange
;
524 if (timeRange
!= null) {
525 if (getRange().getEndTime().getValue() < timeRange
.getEndTime().getValue()) {
534 public void handleCancel() {
535 super.handleCancel();
536 disposeProvider(true);
540 public void handleFailure() {
541 super.handleFailure();
542 disposeProvider(true);
546 // ------------------------------------------------------------------------
547 // ITmfAnalysisModuleWithStateSystems
548 // ------------------------------------------------------------------------
552 public ITmfStateSystem
getStateSystem(String id
) {
553 if (id
.equals(getId())) {
560 public @NonNull Iterable
<@NonNull ITmfStateSystem
> getStateSystems() {
561 ITmfStateSystemBuilder stateSystem
= fStateSystem
;
562 if (stateSystem
== null) {
563 return Collections
.EMPTY_SET
;
565 return Collections
.singleton(stateSystem
);
569 * Signal handler for the TmfTraceRangeUpdatedSignal signal
571 * @param signal The incoming signal
574 public void traceRangeUpdated(final TmfTraceRangeUpdatedSignal signal
) {
575 fTimeRange
= signal
.getRange();
576 ITmfStateProvider stateProvider
= fStateProvider
;
577 synchronized (fRequestSyncObj
) {
578 if (signal
.getTrace() == getTrace() && stateProvider
!= null && stateProvider
.getAssignedStateSystem() != null) {
579 ITmfEventRequest request
= fRequest
;
580 if ((request
== null) || request
.isCompleted()) {
587 private void startRequest() {
588 ITmfStateProvider stateProvider
= fStateProvider
;
589 TmfTimeRange timeRange
= fTimeRange
;
590 if (stateProvider
== null || timeRange
== null) {
593 ITmfEventRequest request
= new StateSystemEventRequest(stateProvider
, timeRange
, fNbRead
);
594 stateProvider
.getTrace().sendRequest(request
);
598 private static boolean isCompleteTrace(ITmfTrace trace
) {
599 return !(trace
instanceof ITmfTraceCompleteness
) || ((ITmfTraceCompleteness
) trace
).isComplete();
602 // ------------------------------------------------------------------------
603 // ITmfPropertiesProvider
604 // ------------------------------------------------------------------------
610 public @NonNull Map
<@NonNull String
, @NonNull String
> getProperties() {
611 Map
<@NonNull String
, @NonNull String
> properties
= super.getProperties();
613 StateSystemBackendType backend
= getBackendType();
614 properties
.put(NonNullUtils
.checkNotNull(Messages
.TmfStateSystemAnalysisModule_PropertiesBackend
), backend
.name());
618 File htFile
= getSsFile();
619 if (htFile
!= null) {
620 if (htFile
.exists()) {
621 properties
.put(NonNullUtils
.checkNotNull(Messages
.TmfStateSystemAnalysisModule_PropertiesFileSize
), FileUtils
.byteCountToDisplaySize(htFile
.length()));
623 properties
.put(NonNullUtils
.checkNotNull(Messages
.TmfStateSystemAnalysisModule_PropertiesFileSize
), NonNullUtils
.checkNotNull(Messages
.TmfStateSystemAnalysisModule_PropertiesAnalysisNotExecuted
));