ae98a632e1f7677526be6ace8e5a5226770c1df0
[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.Map;
20 import java.util.concurrent.CountDownLatch;
21
22 import org.apache.commons.io.FileUtils;
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.NonNull;
27 import org.eclipse.jdt.annotation.Nullable;
28 import org.eclipse.tracecompass.common.core.NonNullUtils;
29 import org.eclipse.tracecompass.internal.tmf.core.statesystem.backends.partial.PartialHistoryBackend;
30 import org.eclipse.tracecompass.internal.tmf.core.statesystem.backends.partial.PartialStateSystem;
31 import org.eclipse.tracecompass.statesystem.core.ITmfStateSystem;
32 import org.eclipse.tracecompass.statesystem.core.ITmfStateSystemBuilder;
33 import org.eclipse.tracecompass.statesystem.core.StateSystemFactory;
34 import org.eclipse.tracecompass.statesystem.core.backend.IStateHistoryBackend;
35 import org.eclipse.tracecompass.statesystem.core.backend.StateHistoryBackendFactory;
36 import org.eclipse.tracecompass.tmf.core.analysis.TmfAbstractAnalysisModule;
37 import org.eclipse.tracecompass.tmf.core.event.ITmfEvent;
38 import org.eclipse.tracecompass.tmf.core.exceptions.TmfTraceException;
39 import org.eclipse.tracecompass.tmf.core.request.ITmfEventRequest;
40 import org.eclipse.tracecompass.tmf.core.request.TmfEventRequest;
41 import org.eclipse.tracecompass.tmf.core.signal.TmfSignalHandler;
42 import org.eclipse.tracecompass.tmf.core.signal.TmfTraceRangeUpdatedSignal;
43 import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimeRange;
44 import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
45 import org.eclipse.tracecompass.tmf.core.trace.ITmfTraceCompleteness;
46 import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager;
47 import org.eclipse.tracecompass.tmf.core.trace.TmfTraceUtils;
48 import org.eclipse.tracecompass.tmf.core.trace.experiment.TmfExperiment;
49
50 /**
51 * Abstract analysis module to generate a state system. It is a base class that
52 * can be used as a shortcut by analysis who just need to build a single state
53 * system with a state provider.
54 *
55 * Analysis implementing this class should only need to provide a state system
56 * and optionally a backend (default to NULL) and, if required, a filename
57 * (defaults to the analysis'ID)
58 *
59 * @author Geneviève Bastien
60 */
61 public abstract class TmfStateSystemAnalysisModule extends TmfAbstractAnalysisModule
62 implements ITmfAnalysisModuleWithStateSystems {
63
64 private static final String EXTENSION = ".ht"; //$NON-NLS-1$
65
66 private final CountDownLatch fInitialized = new CountDownLatch(1);
67 private final Object fRequestSyncObj = new Object();
68
69 @Nullable private ITmfStateSystemBuilder fStateSystem;
70 @Nullable private ITmfStateProvider fStateProvider;
71 @Nullable private IStateHistoryBackend fHtBackend;
72 @Nullable private ITmfEventRequest fRequest;
73 @Nullable private TmfTimeRange fTimeRange = null;
74
75 private int fNbRead = 0;
76 private boolean fInitializationSucceeded;
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 or the module was
106 * not initialized correctly
107 */
108 public static @Nullable ITmfStateSystem getStateSystem(ITmfTrace trace, String moduleId) {
109 TmfStateSystemAnalysisModule module =
110 TmfTraceUtils.getAnalysisModuleOfClass(trace, TmfStateSystemAnalysisModule.class, moduleId);
111 if (module != null) {
112 ITmfStateSystem ss = module.getStateSystem();
113 if (ss != null) {
114 return ss;
115 }
116 IStatus status = module.schedule();
117 if (status.isOK()) {
118 return module.waitForInitialization() ? module.getStateSystem() : null;
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 * @since 2.0
164 */
165 @Override
166 public boolean waitForInitialization() {
167 try {
168 fInitialized.await();
169 } catch (InterruptedException e) {
170 return false;
171 }
172 return fInitializationSucceeded;
173 }
174
175 // ------------------------------------------------------------------------
176 // TmfAbstractAnalysisModule
177 // ------------------------------------------------------------------------
178
179 private @Nullable File getSsFile() {
180 ITmfTrace trace = getTrace();
181 if (trace == null) {
182 return null;
183 }
184 String directory = TmfTraceManager.getSupplementaryFileDir(trace);
185 File htFile = new File(directory + getSsFileName());
186 return htFile;
187 }
188
189 @Override
190 protected boolean executeAnalysis(@Nullable final IProgressMonitor monitor) {
191 IProgressMonitor mon = (monitor == null ? new NullProgressMonitor() : monitor);
192 final ITmfStateProvider provider = createStateProvider();
193
194 String id = getId();
195
196 /* FIXME: State systems should make use of the monitor, to be cancelled */
197 try {
198 /* Get the state system according to backend */
199 StateSystemBackendType backend = getBackendType();
200
201
202 ITmfTrace trace = getTrace();
203 if (trace == null) {
204 // Analysis was cancelled in the meantime
205 analysisReady(false);
206 return false;
207 }
208 switch (backend) {
209 case FULL: {
210 File htFile = getSsFile();
211 if (htFile == null) {
212 return false;
213 }
214 createFullHistory(id, provider, htFile);
215 }
216 break;
217 case PARTIAL: {
218 File htFile = getSsFile();
219 if (htFile == null) {
220 return false;
221 }
222 createPartialHistory(id, provider, htFile);
223 }
224 break;
225 case INMEM:
226 createInMemoryHistory(id, provider);
227 break;
228 case NULL:
229 createNullHistory(id, provider);
230 break;
231 default:
232 break;
233 }
234 } catch (TmfTraceException e) {
235 analysisReady(false);
236 return false;
237 }
238 return !mon.isCanceled();
239 }
240
241 /**
242 * Make the module available and set whether the initialization succeeded or
243 * not. If not, no state system is available and
244 * {@link #waitForInitialization()} should return false.
245 *
246 * @param success
247 * True if the initialization succeeded, false otherwise
248 */
249 private void analysisReady(boolean succeeded) {
250 fInitializationSucceeded = succeeded;
251 fInitialized.countDown();
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 = StateHistoryBackendFactory.createHistoryTreeBackendExistingFile(
290 id, htFile, version);
291 fHtBackend = backend;
292 fStateSystem = StateSystemFactory.newStateSystem(backend, false);
293 analysisReady(true);
294 return;
295 } catch (IOException e) {
296 /*
297 * There was an error opening the existing file. Perhaps it was
298 * corrupted, perhaps it's an old version? We'll just
299 * fall-through and try to build a new one from scratch instead.
300 */
301 }
302 }
303
304 /* Size of the blocking queue to use when building a state history */
305 final int QUEUE_SIZE = 10000;
306
307 try {
308 IStateHistoryBackend backend = StateHistoryBackendFactory.createHistoryTreeBackendNewFile(
309 id, htFile, provider.getVersion(), provider.getStartTime(), QUEUE_SIZE);
310 fHtBackend = backend;
311 fStateSystem = StateSystemFactory.newStateSystem(backend);
312 provider.assignTargetStateSystem(fStateSystem);
313 build(provider);
314 } catch (IOException e) {
315 /*
316 * If it fails here however, it means there was a problem writing to
317 * the disk, so throw a real exception this time.
318 */
319 throw new TmfTraceException(e.toString(), e);
320 }
321 }
322
323 /*
324 * Create a new state system backed with a partial history. A partial
325 * history is similar to a "full" one (which you get with
326 * {@link #newFullHistory}), except that the file on disk is much smaller,
327 * but queries are a bit slower.
328 *
329 * Also note that single-queries are implemented using a full-query
330 * underneath, (which are much slower), so this might not be a good fit for
331 * a use case where you have to do lots of single queries.
332 */
333 private void createPartialHistory(String id, ITmfStateProvider provider, File htPartialFile)
334 throws TmfTraceException {
335 /*
336 * The order of initializations is very tricky (but very important!)
337 * here. We need to follow this pattern:
338 * (1 is done before the call to this method)
339 *
340 * 1- Instantiate realStateProvider
341 * 2- Instantiate realBackend
342 * 3- Instantiate partialBackend, with prereqs:
343 * 3a- Instantiate partialProvider, via realProvider.getNew()
344 * 3b- Instantiate nullBackend (partialSS's backend)
345 * 3c- Instantiate partialSS
346 * 3d- partialProvider.assignSS(partialSS)
347 * 4- Instantiate realSS
348 * 5- partialSS.assignUpstream(realSS)
349 * 6- realProvider.assignSS(realSS)
350 * 7- Call HistoryBuilder(realProvider, realSS, partialBackend) to build the thing.
351 */
352
353 /* Size of the blocking queue to use when building a state history */
354 final int QUEUE_SIZE = 10000;
355
356 final long granularity = 50000;
357
358 /* 2 */
359 IStateHistoryBackend realBackend = null;
360 try {
361 realBackend = StateHistoryBackendFactory.createHistoryTreeBackendNewFile(
362 id, htPartialFile, provider.getVersion(), provider.getStartTime(), QUEUE_SIZE);
363 } catch (IOException e) {
364 throw new TmfTraceException(e.toString(), e);
365 }
366
367 /* 3a */
368 ITmfStateProvider partialProvider = provider.getNewInstance();
369
370 /* 3b-3c, constructor automatically uses a NullBackend */
371 PartialStateSystem pss = new PartialStateSystem();
372
373 /* 3d */
374 partialProvider.assignTargetStateSystem(pss);
375
376 /* 3 */
377 IStateHistoryBackend partialBackend = new PartialHistoryBackend(id + ".partial", partialProvider, pss, realBackend, granularity); //$NON-NLS-1$
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(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 = StateHistoryBackendFactory.createNullBackend(id);
404 fHtBackend = backend;
405 fStateSystem = StateSystemFactory.newStateSystem(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 = StateHistoryBackendFactory.createInMemoryBackend(id, provider.getStartTime());
417 fHtBackend = backend;
418 fStateSystem = StateSystemFactory.newStateSystem(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 (!isCompleteTrace(trace)) {
446 fTimeRange = trace.getTimeRange();
447 }
448
449 fStateProvider = provider;
450 synchronized (fRequestSyncObj) {
451 startRequest();
452 }
453
454 /*
455 * The state system object is now created, we can consider this module
456 * "initialized" (components can retrieve it and start doing queries).
457 */
458 analysisReady(true);
459
460 /*
461 * Block the executeAnalysis() construction is complete (so that the
462 * progress monitor displays that it is running).
463 */
464 try {
465 if (fRequest != null) {
466 fRequest.waitForCompletion();
467 }
468 } catch (InterruptedException e) {
469 e.printStackTrace();
470 }
471 }
472
473 private class StateSystemEventRequest extends TmfEventRequest {
474 private final ITmfStateProvider sci;
475 private final ITmfTrace trace;
476
477 public StateSystemEventRequest(ITmfStateProvider sp, TmfTimeRange timeRange, int index) {
478 super(ITmfEvent.class,
479 timeRange,
480 index,
481 ITmfEventRequest.ALL_DATA,
482 ITmfEventRequest.ExecutionType.BACKGROUND,
483 TmfStateSystemAnalysisModule.this.getDependencyLevel());
484 this.sci = sp;
485 trace = sci.getTrace();
486
487 }
488
489 @Override
490 public void handleData(final ITmfEvent event) {
491 super.handleData(event);
492 if (event.getTrace() == trace) {
493 sci.processEvent(event);
494 } else if (trace instanceof TmfExperiment) {
495 /*
496 * If the request is for an experiment, check if the event is
497 * from one of the child trace
498 */
499 for (ITmfTrace childTrace : ((TmfExperiment) trace).getTraces()) {
500 if (childTrace == event.getTrace()) {
501 sci.processEvent(event);
502 }
503 }
504 }
505 }
506
507 @Override
508 public void handleSuccess() {
509 super.handleSuccess();
510 if (isCompleteTrace(trace)) {
511 disposeProvider(false);
512 } else {
513 fNbRead += getNbRead();
514 synchronized (fRequestSyncObj) {
515 final TmfTimeRange timeRange = fTimeRange;
516 if (timeRange != null) {
517 if (getRange().getEndTime().getValue() < timeRange.getEndTime().getValue()) {
518 startRequest();
519 }
520 }
521 }
522 }
523 }
524
525 @Override
526 public void handleCancel() {
527 super.handleCancel();
528 if (isCompleteTrace(trace)) {
529 disposeProvider(true);
530 }
531 }
532
533 @Override
534 public void handleFailure() {
535 super.handleFailure();
536 disposeProvider(true);
537 }
538 }
539
540 // ------------------------------------------------------------------------
541 // ITmfAnalysisModuleWithStateSystems
542 // ------------------------------------------------------------------------
543
544 @Override
545 @Nullable
546 public ITmfStateSystem getStateSystem(String id) {
547 if (id.equals(getId())) {
548 return fStateSystem;
549 }
550 return null;
551 }
552
553 @Override
554 public @NonNull Iterable<@NonNull ITmfStateSystem> getStateSystems() {
555 ITmfStateSystemBuilder stateSystem = fStateSystem;
556 if (stateSystem == null) {
557 return Collections.EMPTY_SET;
558 }
559 return Collections.singleton(stateSystem);
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
596 // ------------------------------------------------------------------------
597 // ITmfPropertiesProvider
598 // ------------------------------------------------------------------------
599
600 /**
601 * @since 2.0
602 */
603 @Override
604 public @NonNull Map<@NonNull String, @NonNull String> getProperties() {
605 Map<@NonNull String, @NonNull String> properties = super.getProperties();
606
607 StateSystemBackendType backend = getBackendType();
608 properties.put(NonNullUtils.checkNotNull(Messages.TmfStateSystemAnalysisModule_PropertiesBackend), backend.name());
609 switch (backend) {
610 case FULL:
611 case PARTIAL:
612 File htFile = getSsFile();
613 if (htFile != null) {
614 if (htFile.exists()) {
615 properties.put(NonNullUtils.checkNotNull(Messages.TmfStateSystemAnalysisModule_PropertiesFileSize), FileUtils.byteCountToDisplaySize(htFile.length()));
616 } else {
617 properties.put(NonNullUtils.checkNotNull(Messages.TmfStateSystemAnalysisModule_PropertiesFileSize), NonNullUtils.checkNotNull(Messages.TmfStateSystemAnalysisModule_PropertiesAnalysisNotExecuted));
618 }
619 }
620 break;
621 case INMEM:
622 case NULL:
623 default:
624 break;
625
626 }
627 return properties;
628 }
629 }
This page took 0.045145 seconds and 4 git commands to generate.