6b20edb2eb87a48cf5fdb0d39741bf84c4cee857
[deliverable/tracecompass.git] / org.eclipse.linuxtools.tmf.core / src / org / eclipse / linuxtools / tmf / core / statesystem / TmfStateSystemAnalysisModule.java
1 /*******************************************************************************
2 * Copyright (c) 2013 É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.linuxtools.tmf.core.statesystem;
15
16 import java.io.File;
17 import java.io.IOException;
18 import java.util.Collections;
19 import java.util.concurrent.CountDownLatch;
20
21 import org.eclipse.core.runtime.IProgressMonitor;
22 import org.eclipse.core.runtime.NullProgressMonitor;
23 import org.eclipse.jdt.annotation.NonNull;
24 import org.eclipse.jdt.annotation.NonNullByDefault;
25 import org.eclipse.jdt.annotation.Nullable;
26 import org.eclipse.linuxtools.internal.tmf.core.statesystem.StateSystem;
27 import org.eclipse.linuxtools.internal.tmf.core.statesystem.backends.IStateHistoryBackend;
28 import org.eclipse.linuxtools.internal.tmf.core.statesystem.backends.InMemoryBackend;
29 import org.eclipse.linuxtools.internal.tmf.core.statesystem.backends.NullBackend;
30 import org.eclipse.linuxtools.internal.tmf.core.statesystem.backends.historytree.HistoryTreeBackend;
31 import org.eclipse.linuxtools.internal.tmf.core.statesystem.backends.historytree.ThreadedHistoryTreeBackend;
32 import org.eclipse.linuxtools.internal.tmf.core.statesystem.backends.partial.PartialHistoryBackend;
33 import org.eclipse.linuxtools.internal.tmf.core.statesystem.backends.partial.PartialStateSystem;
34 import org.eclipse.linuxtools.tmf.core.analysis.TmfAbstractAnalysisModule;
35 import org.eclipse.linuxtools.tmf.core.event.ITmfEvent;
36 import org.eclipse.linuxtools.tmf.core.exceptions.TmfTraceException;
37 import org.eclipse.linuxtools.tmf.core.request.ITmfEventRequest;
38 import org.eclipse.linuxtools.tmf.core.request.TmfEventRequest;
39 import org.eclipse.linuxtools.tmf.core.timestamp.TmfTimeRange;
40 import org.eclipse.linuxtools.tmf.core.trace.ITmfTrace;
41 import org.eclipse.linuxtools.tmf.core.trace.TmfTraceManager;
42
43 /**
44 * Abstract analysis module to generate a state system. It is a base class that
45 * can be used as a shortcut by analysis who just need to build a single state
46 * system with a state provider.
47 *
48 * Analysis implementing this class should only need to provide a state system
49 * and optionally a backend (default to NULL) and, if required, a filename
50 * (defaults to the analysis'ID)
51 *
52 * @author Geneviève Bastien
53 * @since 3.0
54 */
55 @NonNullByDefault
56 public abstract class TmfStateSystemAnalysisModule extends TmfAbstractAnalysisModule
57 implements ITmfAnalysisModuleWithStateSystems {
58
59 private static final String EXTENSION = ".ht"; //$NON-NLS-1$
60 private static final String UNDEFINED_ID = "undefined"; //$NON-NLS-1$
61
62 private final CountDownLatch fInitialized = new CountDownLatch(1);
63
64 @Nullable private ITmfStateSystemBuilder fStateSystem;
65 @Nullable private ITmfStateProvider fStateProvider;
66 @Nullable private IStateHistoryBackend fHtBackend;
67 @Nullable private ITmfEventRequest fRequest;
68
69 /**
70 * State system backend types
71 *
72 * @author Geneviève Bastien
73 */
74 protected enum StateSystemBackendType {
75 /** Full history in file */
76 FULL,
77 /** In memory state system */
78 INMEM,
79 /** Null history */
80 NULL,
81 /** State system backed with partial history */
82 PARTIAL
83 }
84
85 /**
86 * Get the state provider for this analysis module
87 *
88 * @return the state provider
89 */
90 protected abstract ITmfStateProvider createStateProvider();
91
92 /**
93 * Get the state system backend type used by this module
94 *
95 * @return The {@link StateSystemBackendType}
96 */
97 protected StateSystemBackendType getBackendType() {
98 /* Using full history by default, sub-classes can override */
99 return StateSystemBackendType.FULL;
100 }
101
102 /**
103 * Get the supplementary file name where to save this state system. The
104 * default is the ID of the analysis followed by the extension.
105 *
106 * @return The supplementary file name
107 */
108 protected String getSsFileName() {
109 return getId() + EXTENSION;
110 }
111
112 /**
113 * Get the state system generated by this analysis, or null if it is not yet
114 * created.
115 *
116 * @return The state system
117 */
118 @Nullable
119 public ITmfStateSystem getStateSystem() {
120 return fStateSystem;
121 }
122
123 /**
124 * Block the calling thread until the analysis module has been initialized.
125 * After this method returns, {@link #getStateSystem()} should not return
126 * null anymore.
127 */
128 public void waitForInitialization() {
129 try {
130 fInitialized.await();
131 } catch (InterruptedException e) {}
132 }
133
134 // ------------------------------------------------------------------------
135 // TmfAbstractAnalysisModule
136 // ------------------------------------------------------------------------
137
138 @Override
139 protected boolean executeAnalysis(@Nullable final IProgressMonitor monitor) {
140 IProgressMonitor mon = (monitor == null ? new NullProgressMonitor() : monitor);
141 final ITmfStateProvider provider = createStateProvider();
142
143 String id = getId();
144 if (id == null) {
145 /* The analysis module does not specify an ID, use a generic one */
146 id = UNDEFINED_ID;
147 }
148
149 /* FIXME: State systems should make use of the monitor, to be cancelled */
150 try {
151 /* Get the state system according to backend */
152 StateSystemBackendType backend = getBackendType();
153 String directory;
154 File htFile;
155 switch (backend) {
156 case FULL:
157 directory = TmfTraceManager.getSupplementaryFileDir(getTrace());
158 htFile = new File(directory + getSsFileName());
159 createFullHistory(id, provider, htFile);
160 break;
161 case PARTIAL:
162 directory = TmfTraceManager.getSupplementaryFileDir(getTrace());
163 htFile = new File(directory + getSsFileName());
164 createPartialHistory(id, provider, htFile);
165 break;
166 case INMEM:
167 createInMemoryHistory(id, provider);
168 break;
169 case NULL:
170 createNullHistory(id, provider);
171 break;
172 default:
173 break;
174 }
175 } catch (TmfTraceException e) {
176 return false;
177 }
178 return !mon.isCanceled();
179 }
180
181 @Override
182 protected void canceling() {
183 ITmfEventRequest req = fRequest;
184 if ((req != null) && (!req.isCompleted())) {
185 req.cancel();
186 }
187 }
188
189 @Override
190 public void dispose() {
191 if (fStateSystem != null) {
192 fStateSystem.dispose();
193 }
194 super.dispose();
195 }
196
197 // ------------------------------------------------------------------------
198 // History creation methods
199 // ------------------------------------------------------------------------
200
201 /*
202 * Load the history file matching the target trace. If the file already
203 * exists, it will be opened directly. If not, it will be created from
204 * scratch.
205 */
206 private void createFullHistory(String id, ITmfStateProvider provider, File htFile) throws TmfTraceException {
207
208 /* If the target file already exists, do not rebuild it uselessly */
209 // TODO for now we assume it's complete. Might be a good idea to check
210 // at least if its range matches the trace's range.
211
212 if (htFile.exists()) {
213 /* Load an existing history */
214 final int version = provider.getVersion();
215 try {
216 IStateHistoryBackend backend = new HistoryTreeBackend(htFile, version);
217 fHtBackend = backend;
218 fStateSystem = new StateSystem(id, backend, false);
219 fInitialized.countDown();
220 return;
221 } catch (IOException e) {
222 /*
223 * There was an error opening the existing file. Perhaps it was
224 * corrupted, perhaps it's an old version? We'll just
225 * fall-through and try to build a new one from scratch instead.
226 */
227 }
228 }
229
230 /* Size of the blocking queue to use when building a state history */
231 final int QUEUE_SIZE = 10000;
232
233 try {
234 IStateHistoryBackend backend = new ThreadedHistoryTreeBackend(htFile,
235 provider.getStartTime(), provider.getVersion(), QUEUE_SIZE);
236 fHtBackend = backend;
237 fStateSystem = new StateSystem(id, backend);
238 provider.assignTargetStateSystem(fStateSystem);
239 build(provider);
240 } catch (IOException e) {
241 /*
242 * If it fails here however, it means there was a problem writing to
243 * the disk, so throw a real exception this time.
244 */
245 throw new TmfTraceException(e.toString(), e);
246 }
247 }
248
249 /*
250 * Create a new state system backed with a partial history. A partial
251 * history is similar to a "full" one (which you get with
252 * {@link #newFullHistory}), except that the file on disk is much smaller,
253 * but queries are a bit slower.
254 *
255 * Also note that single-queries are implemented using a full-query
256 * underneath, (which are much slower), so this might not be a good fit for
257 * a use case where you have to do lots of single queries.
258 */
259 private void createPartialHistory(String id, ITmfStateProvider provider, File htPartialFile)
260 throws TmfTraceException {
261 /*
262 * The order of initializations is very tricky (but very important!)
263 * here. We need to follow this pattern:
264 * (1 is done before the call to this method)
265 *
266 * 1- Instantiate realStateProvider
267 * 2- Instantiate realBackend
268 * 3- Instantiate partialBackend, with prereqs:
269 * 3a- Instantiate partialProvider, via realProvider.getNew()
270 * 3b- Instantiate nullBackend (partialSS's backend)
271 * 3c- Instantiate partialSS
272 * 3d- partialProvider.assignSS(partialSS)
273 * 4- Instantiate realSS
274 * 5- partialSS.assignUpstream(realSS)
275 * 6- realProvider.assignSS(realSS)
276 * 7- Call HistoryBuilder(realProvider, realSS, partialBackend) to build the thing.
277 */
278
279 /* Size of the blocking queue to use when building a state history */
280 final int QUEUE_SIZE = 10000;
281
282 final long granularity = 50000;
283
284 /* 2 */
285 IStateHistoryBackend realBackend = null;
286 try {
287 realBackend = new ThreadedHistoryTreeBackend(htPartialFile,
288 provider.getStartTime(), provider.getVersion(), QUEUE_SIZE);
289 } catch (IOException e) {
290 throw new TmfTraceException(e.toString(), e);
291 }
292
293 /* 3a */
294 ITmfStateProvider partialProvider = provider.getNewInstance();
295
296 /* 3b-3c, constructor automatically uses a NullBackend */
297 PartialStateSystem pss = new PartialStateSystem();
298
299 /* 3d */
300 partialProvider.assignTargetStateSystem(pss);
301
302 /* 3 */
303 IStateHistoryBackend partialBackend =
304 new PartialHistoryBackend(partialProvider, pss, realBackend, granularity);
305
306 /* 4 */
307 StateSystem realSS = new StateSystem(id, partialBackend);
308
309 /* 5 */
310 pss.assignUpstream(realSS);
311
312 /* 6 */
313 provider.assignTargetStateSystem(realSS);
314
315 /* 7 */
316 fHtBackend = partialBackend;
317 fStateSystem = realSS;
318
319 build(provider);
320 }
321
322 /*
323 * Create a new state system using a null history back-end. This means that
324 * no history intervals will be saved anywhere, and as such only
325 * {@link ITmfStateSystem#queryOngoingState} will be available.
326 */
327 private void createNullHistory(String id, ITmfStateProvider provider) {
328 IStateHistoryBackend backend = new NullBackend();
329 fHtBackend = backend;
330 fStateSystem = new StateSystem(id, backend);
331 provider.assignTargetStateSystem(fStateSystem);
332 build(provider);
333 }
334
335 /*
336 * Create a new state system using in-memory interval storage. This should
337 * only be done for very small state system, and will be naturally limited
338 * to 2^31 intervals.
339 */
340 private void createInMemoryHistory(String id, ITmfStateProvider provider) {
341 IStateHistoryBackend backend = new InMemoryBackend(provider.getStartTime());
342 fHtBackend = backend;
343 fStateSystem = new StateSystem(id, backend);
344 provider.assignTargetStateSystem(fStateSystem);
345 build(provider);
346 }
347
348 private void disposeProvider(boolean deleteFiles) {
349 ITmfStateProvider provider = fStateProvider;
350 if (provider != null) {
351 provider.dispose();
352 }
353 if (deleteFiles && (fHtBackend != null)) {
354 fHtBackend.removeFiles();
355 }
356 }
357
358 private void build(ITmfStateProvider provider) {
359 if ((fStateSystem == null) || (fHtBackend == null)) {
360 throw new IllegalArgumentException();
361 }
362
363 ITmfEventRequest request = fRequest;
364 if ((request != null) && (!request.isCompleted())) {
365 request.cancel();
366 }
367
368 request = new StateSystemEventRequest(provider);
369 provider.getTrace().sendRequest(request);
370
371 /*
372 * Only now that we've actually started the build, we'll update the
373 * class fields, so that they become visible for other callers.
374 */
375 fStateProvider = provider;
376 fRequest = request;
377
378 /*
379 * The state system object is now created, we can consider this module
380 * "initialized" (components can retrieve it and start doing queries).
381 */
382 fInitialized.countDown();
383
384 /*
385 * Block the executeAnalysis() construction is complete (so that the
386 * progress monitor displays that it is running).
387 */
388 try {
389 request.waitForCompletion();
390 } catch (InterruptedException e) {
391 e.printStackTrace();
392 }
393 }
394
395 private class StateSystemEventRequest extends TmfEventRequest {
396 private final ITmfStateProvider sci;
397 private final ITmfTrace trace;
398
399 public StateSystemEventRequest(ITmfStateProvider sp) {
400 super(sp.getExpectedEventType(),
401 TmfTimeRange.ETERNITY,
402 0,
403 ITmfEventRequest.ALL_DATA,
404 ITmfEventRequest.ExecutionType.BACKGROUND);
405 this.sci = sp;
406
407 // sci.getTrace() will eventually return a @NonNull
408 @SuppressWarnings("null")
409 @NonNull ITmfTrace tr = sci.getTrace();
410
411 this.trace = tr;
412 }
413
414 @Override
415 public void handleData(final @Nullable ITmfEvent event) {
416 super.handleData(event);
417 if (event != null && event.getTrace() == trace) {
418 sci.processEvent(event);
419 }
420 }
421
422 @Override
423 public void handleSuccess() {
424 super.handleSuccess();
425 disposeProvider(false);
426 }
427
428 @Override
429 public void handleCancel() {
430 super.handleCancel();
431 disposeProvider(true);
432 }
433
434 @Override
435 public void handleFailure() {
436 super.handleFailure();
437 disposeProvider(true);
438 }
439 }
440
441 // ------------------------------------------------------------------------
442 // ITmfAnalysisModuleWithStateSystems
443 // ------------------------------------------------------------------------
444
445 @Override
446 @Nullable
447 public ITmfStateSystem getStateSystem(String id) {
448 if (id.equals(getId())) {
449 return fStateSystem;
450 }
451 return null;
452 }
453
454 @Override
455 public Iterable<ITmfStateSystem> getStateSystems() {
456 @SuppressWarnings("null")
457 @NonNull Iterable<ITmfStateSystem> ret = Collections.singleton((ITmfStateSystem) fStateSystem);
458 return ret;
459 }
460 }
This page took 0.046658 seconds and 4 git commands to generate.