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