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