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