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