1 /*******************************************************************************
2 * Copyright (c) 2013 É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
.linuxtools
.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
.NullProgressMonitor
;
23 import org
.eclipse
.jdt
.annotation
.NonNull
;
24 import org
.eclipse
.jdt
.annotation
.NonNullByDefault
;
25 import org
.eclipse
.jdt
.annotation
.Nullable
;
26 import org
.eclipse
.linuxtools
.internal
.tmf
.core
.statesystem
.StateSystem
;
27 import org
.eclipse
.linuxtools
.internal
.tmf
.core
.statesystem
.backends
.IStateHistoryBackend
;
28 import org
.eclipse
.linuxtools
.internal
.tmf
.core
.statesystem
.backends
.InMemoryBackend
;
29 import org
.eclipse
.linuxtools
.internal
.tmf
.core
.statesystem
.backends
.NullBackend
;
30 import org
.eclipse
.linuxtools
.internal
.tmf
.core
.statesystem
.backends
.historytree
.HistoryTreeBackend
;
31 import org
.eclipse
.linuxtools
.internal
.tmf
.core
.statesystem
.backends
.historytree
.ThreadedHistoryTreeBackend
;
32 import org
.eclipse
.linuxtools
.internal
.tmf
.core
.statesystem
.backends
.partial
.PartialHistoryBackend
;
33 import org
.eclipse
.linuxtools
.internal
.tmf
.core
.statesystem
.backends
.partial
.PartialStateSystem
;
34 import org
.eclipse
.linuxtools
.tmf
.core
.analysis
.TmfAbstractAnalysisModule
;
35 import org
.eclipse
.linuxtools
.tmf
.core
.event
.ITmfEvent
;
36 import org
.eclipse
.linuxtools
.tmf
.core
.exceptions
.TmfTraceException
;
37 import org
.eclipse
.linuxtools
.tmf
.core
.request
.ITmfEventRequest
;
38 import org
.eclipse
.linuxtools
.tmf
.core
.request
.TmfEventRequest
;
39 import org
.eclipse
.linuxtools
.tmf
.core
.timestamp
.TmfTimeRange
;
40 import org
.eclipse
.linuxtools
.tmf
.core
.trace
.ITmfTrace
;
41 import org
.eclipse
.linuxtools
.tmf
.core
.trace
.TmfTraceManager
;
44 * Abstract analysis module to generate a state system. It is a base class that
45 * can be used as a shortcut by analysis who just need to build a single state
46 * system with a state provider.
48 * Analysis implementing this class should only need to provide a state system
49 * and optionally a backend (default to NULL) and, if required, a filename
50 * (defaults to the analysis'ID)
52 * @author Geneviève Bastien
56 public abstract class TmfStateSystemAnalysisModule
extends TmfAbstractAnalysisModule
57 implements ITmfAnalysisModuleWithStateSystems
{
59 private static final String EXTENSION
= ".ht"; //$NON-NLS-1$
60 private static final String UNDEFINED_ID
= "undefined"; //$NON-NLS-1$
62 private final CountDownLatch fInitialized
= new CountDownLatch(1);
64 @Nullable private ITmfStateSystemBuilder fStateSystem
;
65 @Nullable private ITmfStateProvider fStateProvider
;
66 @Nullable private IStateHistoryBackend fHtBackend
;
67 @Nullable private ITmfEventRequest fRequest
;
70 * State system backend types
72 * @author Geneviève Bastien
74 protected enum StateSystemBackendType
{
75 /** Full history in file */
77 /** In memory state system */
81 /** State system backed with partial history */
86 * Get the state provider for this analysis module
88 * @return the state provider
90 protected abstract ITmfStateProvider
createStateProvider();
93 * Get the state system backend type used by this module
95 * @return The {@link StateSystemBackendType}
97 protected abstract StateSystemBackendType
getBackendType();
100 * Get the supplementary file name where to save this state system. The
101 * default is the ID of the analysis followed by the extension.
103 * @return The supplementary file name
105 protected String
getSsFileName() {
106 return getId() + EXTENSION
;
110 * Get the state system generated by this analysis, or null if it is not yet
113 * @return The state system
116 public ITmfStateSystem
getStateSystem() {
121 * Block the calling thread until the analysis module has been initialized.
122 * After this method returns, {@link #getStateSystem()} should not return
125 public void waitForInitialization() {
127 fInitialized
.await();
128 } catch (InterruptedException e
) {}
131 // ------------------------------------------------------------------------
132 // TmfAbstractAnalysisModule
133 // ------------------------------------------------------------------------
136 protected boolean executeAnalysis(@Nullable final IProgressMonitor monitor
) {
137 IProgressMonitor mon
= (monitor
== null ?
new NullProgressMonitor() : monitor
);
138 final ITmfStateProvider provider
= createStateProvider();
142 /* The analysis module does not specify an ID, use a generic one */
146 /* FIXME: State systems should make use of the monitor, to be cancelled */
148 /* Get the state system according to backend */
149 StateSystemBackendType backend
= getBackendType();
154 directory
= TmfTraceManager
.getSupplementaryFileDir(getTrace());
155 htFile
= new File(directory
+ getSsFileName());
156 createFullHistory(id
, provider
, htFile
);
159 directory
= TmfTraceManager
.getSupplementaryFileDir(getTrace());
160 htFile
= new File(directory
+ getSsFileName());
161 createPartialHistory(id
, provider
, htFile
);
164 createInMemoryHistory(id
, provider
);
167 createNullHistory(id
, provider
);
172 } catch (TmfTraceException e
) {
175 return !mon
.isCanceled();
179 protected void canceling() {
180 ITmfEventRequest req
= fRequest
;
181 if ((req
!= null) && (!req
.isCompleted())) {
187 public void dispose() {
188 if (fStateSystem
!= null) {
189 fStateSystem
.dispose();
194 // ------------------------------------------------------------------------
195 // History creation methods
196 // ------------------------------------------------------------------------
199 * Load the history file matching the target trace. If the file already
200 * exists, it will be opened directly. If not, it will be created from
203 private void createFullHistory(String id
, ITmfStateProvider provider
, File htFile
) throws TmfTraceException
{
205 /* If the target file already exists, do not rebuild it uselessly */
206 // TODO for now we assume it's complete. Might be a good idea to check
207 // at least if its range matches the trace's range.
209 if (htFile
.exists()) {
210 /* Load an existing history */
211 final int version
= provider
.getVersion();
213 IStateHistoryBackend backend
= new HistoryTreeBackend(htFile
, version
);
214 fHtBackend
= backend
;
215 fStateSystem
= new StateSystem(id
, backend
, false);
216 fInitialized
.countDown();
218 } catch (IOException e
) {
220 * There was an error opening the existing file. Perhaps it was
221 * corrupted, perhaps it's an old version? We'll just
222 * fall-through and try to build a new one from scratch instead.
227 /* Size of the blocking queue to use when building a state history */
228 final int QUEUE_SIZE
= 10000;
231 IStateHistoryBackend backend
= new ThreadedHistoryTreeBackend(htFile
,
232 provider
.getStartTime(), provider
.getVersion(), QUEUE_SIZE
);
233 fHtBackend
= backend
;
234 fStateSystem
= new StateSystem(id
, backend
);
235 provider
.assignTargetStateSystem(fStateSystem
);
237 } catch (IOException e
) {
239 * If it fails here however, it means there was a problem writing to
240 * the disk, so throw a real exception this time.
242 throw new TmfTraceException(e
.toString(), e
);
247 * Create a new state system backed with a partial history. A partial
248 * history is similar to a "full" one (which you get with
249 * {@link #newFullHistory}), except that the file on disk is much smaller,
250 * but queries are a bit slower.
252 * Also note that single-queries are implemented using a full-query
253 * underneath, (which are much slower), so this might not be a good fit for
254 * a use case where you have to do lots of single queries.
256 private void createPartialHistory(String id
, ITmfStateProvider provider
, File htPartialFile
)
257 throws TmfTraceException
{
259 * The order of initializations is very tricky (but very important!)
260 * here. We need to follow this pattern:
261 * (1 is done before the call to this method)
263 * 1- Instantiate realStateProvider
264 * 2- Instantiate realBackend
265 * 3- Instantiate partialBackend, with prereqs:
266 * 3a- Instantiate partialProvider, via realProvider.getNew()
267 * 3b- Instantiate nullBackend (partialSS's backend)
268 * 3c- Instantiate partialSS
269 * 3d- partialProvider.assignSS(partialSS)
270 * 4- Instantiate realSS
271 * 5- partialSS.assignUpstream(realSS)
272 * 6- realProvider.assignSS(realSS)
273 * 7- Call HistoryBuilder(realProvider, realSS, partialBackend) to build the thing.
276 /* Size of the blocking queue to use when building a state history */
277 final int QUEUE_SIZE
= 10000;
279 final long granularity
= 50000;
282 IStateHistoryBackend realBackend
= null;
284 realBackend
= new ThreadedHistoryTreeBackend(htPartialFile
,
285 provider
.getStartTime(), provider
.getVersion(), QUEUE_SIZE
);
286 } catch (IOException e
) {
287 throw new TmfTraceException(e
.toString(), e
);
291 ITmfStateProvider partialProvider
= provider
.getNewInstance();
293 /* 3b-3c, constructor automatically uses a NullBackend */
294 PartialStateSystem pss
= new PartialStateSystem();
297 partialProvider
.assignTargetStateSystem(pss
);
300 IStateHistoryBackend partialBackend
=
301 new PartialHistoryBackend(partialProvider
, pss
, realBackend
, granularity
);
304 StateSystem realSS
= new StateSystem(id
, partialBackend
);
307 pss
.assignUpstream(realSS
);
310 provider
.assignTargetStateSystem(realSS
);
313 fHtBackend
= partialBackend
;
314 fStateSystem
= realSS
;
320 * Create a new state system using a null history back-end. This means that
321 * no history intervals will be saved anywhere, and as such only
322 * {@link ITmfStateSystem#queryOngoingState} will be available.
324 private void createNullHistory(String id
, ITmfStateProvider provider
) {
325 IStateHistoryBackend backend
= new NullBackend();
326 fHtBackend
= backend
;
327 fStateSystem
= new StateSystem(id
, backend
);
328 provider
.assignTargetStateSystem(fStateSystem
);
333 * Create a new state system using in-memory interval storage. This should
334 * only be done for very small state system, and will be naturally limited
337 private void createInMemoryHistory(String id
, ITmfStateProvider provider
) {
338 IStateHistoryBackend backend
= new InMemoryBackend(provider
.getStartTime());
339 fHtBackend
= backend
;
340 fStateSystem
= new StateSystem(id
, backend
);
341 provider
.assignTargetStateSystem(fStateSystem
);
345 private void disposeProvider(boolean deleteFiles
) {
346 ITmfStateProvider provider
= fStateProvider
;
347 if (provider
!= null) {
350 if (deleteFiles
&& (fHtBackend
!= null)) {
351 fHtBackend
.removeFiles();
355 private void build(ITmfStateProvider provider
) {
356 if ((fStateSystem
== null) || (fHtBackend
== null)) {
357 throw new IllegalArgumentException();
360 ITmfEventRequest request
= fRequest
;
361 if ((request
!= null) && (!request
.isCompleted())) {
365 request
= new StateSystemEventRequest(provider
);
366 provider
.getTrace().sendRequest(request
);
369 * Only now that we've actually started the build, we'll update the
370 * class fields, so that they become visible for other callers.
372 fStateProvider
= provider
;
376 * The state system object is now created, we can consider this module
377 * "initialized" (components can retrieve it and start doing queries).
379 fInitialized
.countDown();
382 * Block the executeAnalysis() construction is complete (so that the
383 * progress monitor displays that it is running).
386 request
.waitForCompletion();
387 } catch (InterruptedException e
) {
392 private class StateSystemEventRequest
extends TmfEventRequest
{
393 private final ITmfStateProvider sci
;
394 private final ITmfTrace trace
;
396 public StateSystemEventRequest(ITmfStateProvider sp
) {
397 super(sp
.getExpectedEventType(),
398 TmfTimeRange
.ETERNITY
,
400 ITmfEventRequest
.ALL_DATA
,
401 ITmfEventRequest
.ExecutionType
.BACKGROUND
);
404 // sci.getTrace() will eventually return a @NonNull
405 @SuppressWarnings("null")
406 @NonNull ITmfTrace tr
= sci
.getTrace();
412 public void handleData(final @Nullable ITmfEvent event
) {
413 super.handleData(event
);
414 if (event
!= null && event
.getTrace() == trace
) {
415 sci
.processEvent(event
);
420 public void handleSuccess() {
421 super.handleSuccess();
422 disposeProvider(false);
426 public void handleCancel() {
427 super.handleCancel();
428 disposeProvider(true);
432 public void handleFailure() {
433 super.handleFailure();
434 disposeProvider(true);
438 // ------------------------------------------------------------------------
439 // ITmfAnalysisModuleWithStateSystems
440 // ------------------------------------------------------------------------
444 public ITmfStateSystem
getStateSystem(String id
) {
445 if (id
.equals(getId())) {
453 public String
getStateSystemId(ITmfStateSystem ss
) {
458 public Iterable
<ITmfStateSystem
> getStateSystems() {
459 @SuppressWarnings("null")
460 @NonNull Iterable
<ITmfStateSystem
> ret
= Collections
.singleton((ITmfStateSystem
) fStateSystem
);