TMF: Check if state system is available before scheduling analysis
[deliverable/tracecompass.git] / org.eclipse.tracecompass.tmf.core / src / org / eclipse / tracecompass / tmf / core / statesystem / TmfStateSystemAnalysisModule.java
1 /*******************************************************************************
2 * Copyright (c) 2013, 2015 École Polytechnique de Montréal
3 *
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
8 *
9 * Contributors:
10 * Geneviève Bastien - Initial API and implementation
11 * Bernd Hufmann - Integrated history builder functionality
12 *******************************************************************************/
13
14 package org.eclipse.tracecompass.tmf.core.statesystem;
15
16 import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull;
17
18 import java.io.File;
19 import java.io.IOException;
20 import java.util.Collections;
21 import java.util.concurrent.CountDownLatch;
22
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.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;
50
51 /**
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.
55 *
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)
59 *
60 * @author Geneviève Bastien
61 */
62 public abstract class TmfStateSystemAnalysisModule extends TmfAbstractAnalysisModule
63 implements ITmfAnalysisModuleWithStateSystems {
64
65 private static final String EXTENSION = ".ht"; //$NON-NLS-1$
66
67 private final CountDownLatch fInitialized = new CountDownLatch(1);
68 private final Object fRequestSyncObj = new Object();
69
70 @Nullable private ITmfStateSystemBuilder fStateSystem;
71 @Nullable private ITmfStateProvider fStateProvider;
72 @Nullable private IStateHistoryBackend fHtBackend;
73 @Nullable private ITmfEventRequest fRequest;
74 @Nullable private TmfTimeRange fTimeRange = null;
75
76 private int fNbRead = 0;
77
78 /**
79 * State system backend types
80 *
81 * @author Geneviève Bastien
82 */
83 protected enum StateSystemBackendType {
84 /** Full history in file */
85 FULL,
86 /** In memory state system */
87 INMEM,
88 /** Null history */
89 NULL,
90 /** State system backed with partial history */
91 PARTIAL
92 }
93
94 /**
95 * Retrieve a state system belonging to trace, by passing the ID of the
96 * relevant analysis module.
97 *
98 * This will start the execution of the analysis module, and start the
99 * construction of the state system, if needed.
100 *
101 * @param trace
102 * The trace for which you want the state system
103 * @param moduleId
104 * The ID of the state system analysis module
105 * @return The state system, or null if there was no match
106 */
107 public static @Nullable ITmfStateSystem getStateSystem(ITmfTrace trace, String moduleId) {
108 TmfStateSystemAnalysisModule module =
109 TmfTraceUtils.getAnalysisModuleOfClass(trace, TmfStateSystemAnalysisModule.class, moduleId);
110 if (module != null) {
111 ITmfStateSystem ss = module.getStateSystem();
112 if (ss != null) {
113 return ss;
114 }
115 IStatus status = module.schedule();
116 if (status.isOK()) {
117 module.waitForInitialization();
118 return module.getStateSystem();
119 }
120 }
121 return null;
122 }
123
124 /**
125 * Get the state provider for this analysis module
126 *
127 * @return the state provider
128 */
129 protected abstract ITmfStateProvider createStateProvider();
130
131 /**
132 * Get the state system backend type used by this module
133 *
134 * @return The {@link StateSystemBackendType}
135 */
136 protected StateSystemBackendType getBackendType() {
137 /* Using full history by default, sub-classes can override */
138 return StateSystemBackendType.FULL;
139 }
140
141 /**
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.
144 *
145 * @return The supplementary file name
146 */
147 protected String getSsFileName() {
148 return getId() + EXTENSION;
149 }
150
151 /**
152 * Get the state system generated by this analysis, or null if it is not yet
153 * created.
154 *
155 * @return The state system
156 */
157 @Nullable
158 public ITmfStateSystem getStateSystem() {
159 return fStateSystem;
160 }
161
162 /**
163 * Block the calling thread until the analysis module has been initialized.
164 * After this method returns, {@link #getStateSystem()} should not return
165 * null anymore.
166 */
167 public void waitForInitialization() {
168 try {
169 fInitialized.await();
170 } catch (InterruptedException e) {}
171 }
172
173 // ------------------------------------------------------------------------
174 // TmfAbstractAnalysisModule
175 // ------------------------------------------------------------------------
176
177 @Override
178 protected boolean executeAnalysis(@Nullable final IProgressMonitor monitor) {
179 IProgressMonitor mon = (monitor == null ? new NullProgressMonitor() : monitor);
180 final ITmfStateProvider provider = createStateProvider();
181
182 String id = getId();
183
184 /* FIXME: State systems should make use of the monitor, to be cancelled */
185 try {
186 /* Get the state system according to backend */
187 StateSystemBackendType backend = getBackendType();
188 String directory;
189 File htFile;
190
191 ITmfTrace trace = getTrace();
192 if (trace == null) {
193 // Analysis was cancelled in the meantime
194 fInitialized.countDown();
195 return false;
196 }
197 switch (backend) {
198 case FULL:
199 directory = TmfTraceManager.getSupplementaryFileDir(trace);
200 htFile = new File(directory + getSsFileName());
201 createFullHistory(id, provider, htFile);
202 break;
203 case PARTIAL:
204 directory = TmfTraceManager.getSupplementaryFileDir(trace);
205 htFile = new File(directory + getSsFileName());
206 createPartialHistory(id, provider, htFile);
207 break;
208 case INMEM:
209 createInMemoryHistory(id, provider);
210 break;
211 case NULL:
212 createNullHistory(id, provider);
213 break;
214 default:
215 break;
216 }
217 } catch (TmfTraceException e) {
218 fInitialized.countDown();
219 return false;
220 }
221 return !mon.isCanceled();
222 }
223
224 @Override
225 protected void canceling() {
226 ITmfEventRequest req = fRequest;
227 if ((req != null) && (!req.isCompleted())) {
228 req.cancel();
229 }
230 }
231
232 @Override
233 public void dispose() {
234 super.dispose();
235 if (fStateSystem != null) {
236 fStateSystem.dispose();
237 }
238 }
239
240 // ------------------------------------------------------------------------
241 // History creation methods
242 // ------------------------------------------------------------------------
243
244 /*
245 * Load the history file matching the target trace. If the file already
246 * exists, it will be opened directly. If not, it will be created from
247 * scratch.
248 */
249 private void createFullHistory(String id, ITmfStateProvider provider, File htFile) throws TmfTraceException {
250
251 /* If the target file already exists, do not rebuild it uselessly */
252 // TODO for now we assume it's complete. Might be a good idea to check
253 // at least if its range matches the trace's range.
254
255 if (htFile.exists()) {
256 /* Load an existing history */
257 final int version = provider.getVersion();
258 try {
259 IStateHistoryBackend backend = new HistoryTreeBackend(id, htFile, version);
260 fHtBackend = backend;
261 fStateSystem = StateSystemFactory.newStateSystem(backend, false);
262 fInitialized.countDown();
263 return;
264 } catch (IOException e) {
265 /*
266 * There was an error opening the existing file. Perhaps it was
267 * corrupted, perhaps it's an old version? We'll just
268 * fall-through and try to build a new one from scratch instead.
269 */
270 }
271 }
272
273 /* Size of the blocking queue to use when building a state history */
274 final int QUEUE_SIZE = 10000;
275
276 try {
277 IStateHistoryBackend backend = new ThreadedHistoryTreeBackend(id, htFile,
278 provider.getStartTime(), provider.getVersion(), QUEUE_SIZE);
279 fHtBackend = backend;
280 fStateSystem = StateSystemFactory.newStateSystem(backend);
281 provider.assignTargetStateSystem(fStateSystem);
282 build(provider);
283 } catch (IOException e) {
284 /*
285 * If it fails here however, it means there was a problem writing to
286 * the disk, so throw a real exception this time.
287 */
288 throw new TmfTraceException(e.toString(), e);
289 }
290 }
291
292 /*
293 * Create a new state system backed with a partial history. A partial
294 * history is similar to a "full" one (which you get with
295 * {@link #newFullHistory}), except that the file on disk is much smaller,
296 * but queries are a bit slower.
297 *
298 * Also note that single-queries are implemented using a full-query
299 * underneath, (which are much slower), so this might not be a good fit for
300 * a use case where you have to do lots of single queries.
301 */
302 private void createPartialHistory(String id, ITmfStateProvider provider, File htPartialFile)
303 throws TmfTraceException {
304 /*
305 * The order of initializations is very tricky (but very important!)
306 * here. We need to follow this pattern:
307 * (1 is done before the call to this method)
308 *
309 * 1- Instantiate realStateProvider
310 * 2- Instantiate realBackend
311 * 3- Instantiate partialBackend, with prereqs:
312 * 3a- Instantiate partialProvider, via realProvider.getNew()
313 * 3b- Instantiate nullBackend (partialSS's backend)
314 * 3c- Instantiate partialSS
315 * 3d- partialProvider.assignSS(partialSS)
316 * 4- Instantiate realSS
317 * 5- partialSS.assignUpstream(realSS)
318 * 6- realProvider.assignSS(realSS)
319 * 7- Call HistoryBuilder(realProvider, realSS, partialBackend) to build the thing.
320 */
321
322 /* Size of the blocking queue to use when building a state history */
323 final int QUEUE_SIZE = 10000;
324
325 final long granularity = 50000;
326
327 /* 2 */
328 IStateHistoryBackend realBackend = null;
329 try {
330 realBackend = new ThreadedHistoryTreeBackend(id, htPartialFile,
331 provider.getStartTime(), provider.getVersion(), QUEUE_SIZE);
332 } catch (IOException e) {
333 throw new TmfTraceException(e.toString(), e);
334 }
335
336 /* 3a */
337 ITmfStateProvider partialProvider = provider.getNewInstance();
338
339 /* 3b-3c, constructor automatically uses a NullBackend */
340 PartialStateSystem pss = new PartialStateSystem();
341
342 /* 3d */
343 partialProvider.assignTargetStateSystem(pss);
344
345 /* 3 */
346 String partialId = new String(id + ".partial"); //$NON-NLS-1$
347 IStateHistoryBackend partialBackend =
348 new PartialHistoryBackend(partialId, partialProvider, pss, realBackend, granularity);
349
350 /* 4 */
351 @SuppressWarnings("restriction")
352 org.eclipse.tracecompass.internal.statesystem.core.StateSystem realSS =
353 (org.eclipse.tracecompass.internal.statesystem.core.StateSystem) StateSystemFactory.newStateSystem(partialBackend);
354
355 /* 5 */
356 pss.assignUpstream(realSS);
357
358 /* 6 */
359 provider.assignTargetStateSystem(realSS);
360
361 /* 7 */
362 fHtBackend = partialBackend;
363 fStateSystem = realSS;
364
365 build(provider);
366 }
367
368 /*
369 * Create a new state system using a null history back-end. This means that
370 * no history intervals will be saved anywhere, and as such only
371 * {@link ITmfStateSystem#queryOngoingState} will be available.
372 */
373 private void createNullHistory(String id, ITmfStateProvider provider) {
374 IStateHistoryBackend backend = new NullBackend(id);
375 fHtBackend = backend;
376 fStateSystem = StateSystemFactory.newStateSystem(backend);
377 provider.assignTargetStateSystem(fStateSystem);
378 build(provider);
379 }
380
381 /*
382 * Create a new state system using in-memory interval storage. This should
383 * only be done for very small state system, and will be naturally limited
384 * to 2^31 intervals.
385 */
386 private void createInMemoryHistory(String id, ITmfStateProvider provider) {
387 IStateHistoryBackend backend = new InMemoryBackend(id, provider.getStartTime());
388 fHtBackend = backend;
389 fStateSystem = StateSystemFactory.newStateSystem(backend);
390 provider.assignTargetStateSystem(fStateSystem);
391 build(provider);
392 }
393
394 private void disposeProvider(boolean deleteFiles) {
395 ITmfStateProvider provider = fStateProvider;
396 if (provider != null) {
397 provider.dispose();
398 }
399 if (deleteFiles && (fHtBackend != null)) {
400 fHtBackend.removeFiles();
401 }
402 }
403
404 private void build(ITmfStateProvider provider) {
405 if ((fStateSystem == null) || (fHtBackend == null)) {
406 throw new IllegalArgumentException();
407 }
408
409 ITmfEventRequest request = fRequest;
410 if ((request != null) && (!request.isCompleted())) {
411 request.cancel();
412 }
413
414 fTimeRange = TmfTimeRange.ETERNITY;
415 final ITmfTrace trace = provider.getTrace();
416 if (!isCompleteTrace(trace)) {
417 fTimeRange = trace.getTimeRange();
418 }
419
420 fStateProvider = provider;
421 synchronized (fRequestSyncObj) {
422 startRequest();
423 }
424
425 /*
426 * The state system object is now created, we can consider this module
427 * "initialized" (components can retrieve it and start doing queries).
428 */
429 fInitialized.countDown();
430
431 /*
432 * Block the executeAnalysis() construction is complete (so that the
433 * progress monitor displays that it is running).
434 */
435 try {
436 if (fRequest != null) {
437 fRequest.waitForCompletion();
438 }
439 } catch (InterruptedException e) {
440 e.printStackTrace();
441 }
442 }
443
444 private class StateSystemEventRequest extends TmfEventRequest {
445 private final ITmfStateProvider sci;
446 private final ITmfTrace trace;
447
448 public StateSystemEventRequest(ITmfStateProvider sp, TmfTimeRange timeRange, int index) {
449 super(ITmfEvent.class,
450 timeRange,
451 index,
452 ITmfEventRequest.ALL_DATA,
453 ITmfEventRequest.ExecutionType.BACKGROUND);
454 this.sci = sp;
455
456 // sci.getTrace() will eventually return a @NonNull
457 trace = checkNotNull(sci.getTrace());
458
459 }
460
461 @Override
462 public void handleData(final ITmfEvent event) {
463 super.handleData(event);
464 if (event.getTrace() == trace) {
465 sci.processEvent(event);
466 } else if (trace instanceof TmfExperiment) {
467 /*
468 * If the request is for an experiment, check if the event is
469 * from one of the child trace
470 */
471 for (ITmfTrace childTrace : ((TmfExperiment) trace).getTraces()) {
472 if (childTrace == event.getTrace()) {
473 sci.processEvent(event);
474 }
475 }
476 }
477 }
478
479 @Override
480 public void handleSuccess() {
481 super.handleSuccess();
482 if (isCompleteTrace(trace)) {
483 disposeProvider(false);
484 } else {
485 fNbRead += getNbRead();
486 synchronized (fRequestSyncObj) {
487 final TmfTimeRange timeRange = fTimeRange;
488 if (timeRange != null) {
489 if (getRange().getEndTime().getValue() < timeRange.getEndTime().getValue()) {
490 startRequest();
491 }
492 }
493 }
494 }
495 }
496
497 @Override
498 public void handleCancel() {
499 super.handleCancel();
500 if (isCompleteTrace(trace)) {
501 disposeProvider(true);
502 }
503 }
504
505 @Override
506 public void handleFailure() {
507 super.handleFailure();
508 disposeProvider(true);
509 }
510 }
511
512 // ------------------------------------------------------------------------
513 // ITmfAnalysisModuleWithStateSystems
514 // ------------------------------------------------------------------------
515
516 @Override
517 @Nullable
518 public ITmfStateSystem getStateSystem(String id) {
519 if (id.equals(getId())) {
520 return fStateSystem;
521 }
522 return null;
523 }
524
525 @Override
526 public Iterable<ITmfStateSystem> getStateSystems() {
527 return checkNotNull(Collections.<ITmfStateSystem> singleton(fStateSystem));
528 }
529
530 /**
531 * Signal handler for the TmfTraceRangeUpdatedSignal signal
532 *
533 * @param signal The incoming signal
534 */
535 @TmfSignalHandler
536 public void traceRangeUpdated(final TmfTraceRangeUpdatedSignal signal) {
537 fTimeRange = signal.getRange();
538 ITmfStateProvider stateProvider = fStateProvider;
539 synchronized (fRequestSyncObj) {
540 if (signal.getTrace() == getTrace() && stateProvider != null && stateProvider.getAssignedStateSystem() != null) {
541 ITmfEventRequest request = fRequest;
542 if ((request == null) || request.isCompleted()) {
543 startRequest();
544 }
545 }
546 }
547 }
548
549 private void startRequest() {
550 ITmfStateProvider stateProvider = fStateProvider;
551 TmfTimeRange timeRange = fTimeRange;
552 if (stateProvider == null || timeRange == null) {
553 return;
554 }
555 ITmfEventRequest request = new StateSystemEventRequest(stateProvider, timeRange, fNbRead);
556 stateProvider.getTrace().sendRequest(request);
557 fRequest = request;
558 }
559
560 private static boolean isCompleteTrace(ITmfTrace trace) {
561 return !(trace instanceof ITmfTraceCompleteness) || ((ITmfTraceCompleteness) trace).isComplete();
562 }
563 }
This page took 0.046082 seconds and 5 git commands to generate.