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