tmf: Move TmfExperiment to its own package
[deliverable/tracecompass.git] / org.eclipse.tracecompass.tmf.core / src / org / eclipse / tracecompass / tmf / core / trace / experiment / TmfExperiment.java
1 /*******************************************************************************
2 * Copyright (c) 2009, 2014 Ericsson, É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 * Francois Chouinard - Initial API and implementation
11 * Francois Chouinard - Updated as per TMF Trace Model 1.0
12 * Patrick Tasse - Updated for removal of context clone
13 * Patrick Tasse - Updated for ranks in experiment location
14 * Geneviève Bastien - Added support of experiment synchronization
15 * Added the initExperiment method and default constructor
16 *******************************************************************************/
17
18 package org.eclipse.tracecompass.tmf.core.trace.experiment;
19
20 import java.io.File;
21 import java.nio.ByteBuffer;
22 import java.util.Arrays;
23
24 import org.eclipse.core.resources.IProject;
25 import org.eclipse.core.resources.IResource;
26 import org.eclipse.core.runtime.CoreException;
27 import org.eclipse.core.runtime.IStatus;
28 import org.eclipse.core.runtime.MultiStatus;
29 import org.eclipse.core.runtime.Status;
30 import org.eclipse.jdt.annotation.Nullable;
31 import org.eclipse.tracecompass.internal.tmf.core.Activator;
32 import org.eclipse.tracecompass.internal.tmf.core.trace.experiment.TmfExperimentContext;
33 import org.eclipse.tracecompass.internal.tmf.core.trace.experiment.TmfExperimentLocation;
34 import org.eclipse.tracecompass.internal.tmf.core.trace.experiment.TmfLocationArray;
35 import org.eclipse.tracecompass.tmf.core.TmfCommonConstants;
36 import org.eclipse.tracecompass.tmf.core.event.ITmfEvent;
37 import org.eclipse.tracecompass.tmf.core.exceptions.TmfTraceException;
38 import org.eclipse.tracecompass.tmf.core.request.ITmfEventRequest;
39 import org.eclipse.tracecompass.tmf.core.signal.TmfSignalHandler;
40 import org.eclipse.tracecompass.tmf.core.signal.TmfTraceOpenedSignal;
41 import org.eclipse.tracecompass.tmf.core.signal.TmfTraceRangeUpdatedSignal;
42 import org.eclipse.tracecompass.tmf.core.signal.TmfTraceSynchronizedSignal;
43 import org.eclipse.tracecompass.tmf.core.synchronization.SynchronizationAlgorithm;
44 import org.eclipse.tracecompass.tmf.core.synchronization.SynchronizationManager;
45 import org.eclipse.tracecompass.tmf.core.timestamp.ITmfTimestamp;
46 import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimeRange;
47 import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestamp;
48 import org.eclipse.tracecompass.tmf.core.trace.ITmfContext;
49 import org.eclipse.tracecompass.tmf.core.trace.ITmfEventParser;
50 import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
51 import org.eclipse.tracecompass.tmf.core.trace.TmfTrace;
52 import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager;
53 import org.eclipse.tracecompass.tmf.core.trace.indexer.ITmfPersistentlyIndexable;
54 import org.eclipse.tracecompass.tmf.core.trace.indexer.ITmfTraceIndexer;
55 import org.eclipse.tracecompass.tmf.core.trace.indexer.TmfBTreeTraceIndexer;
56 import org.eclipse.tracecompass.tmf.core.trace.location.ITmfLocation;
57
58 /**
59 * TmfExperiment presents a time-ordered, unified view of a set of ITmfTrace:s
60 * that are part of a tracing experiment.
61 *
62 * @version 1.0
63 * @author Francois Chouinard
64 */
65 public class TmfExperiment extends TmfTrace implements ITmfEventParser, ITmfPersistentlyIndexable {
66
67 // ------------------------------------------------------------------------
68 // Constants
69 // ------------------------------------------------------------------------
70
71 /**
72 * The file name of the Synchronization
73 *
74 * @since 3.0
75 * @deprecated This file name shouldn't be used directly anymore. All
76 * synchronization files have been moved to a folder and you
77 * should use the {@link #getSynchronizationFolder(boolean)}
78 * method to return the path to this folder.
79 */
80 @Deprecated
81 public static final String SYNCHRONIZATION_FILE_NAME = "synchronization.bin"; //$NON-NLS-1$
82
83 /**
84 * The name of the directory containing trace synchronization data. This
85 * directory typically will be preserved when traces are synchronized.
86 * Analysis involved in synchronization can put their supplementary files in
87 * there so they are not deleted when synchronized traces are copied.
88 */
89 private static final String SYNCHRONIZATION_DIRECTORY = "sync_data"; //$NON-NLS-1$
90
91 /**
92 * The default index page size
93 */
94 public static final int DEFAULT_INDEX_PAGE_SIZE = 5000;
95
96 // ------------------------------------------------------------------------
97 // Attributes
98 // ------------------------------------------------------------------------
99
100 /**
101 * The set of traces that constitute the experiment
102 */
103 protected ITmfTrace[] fTraces;
104
105 /**
106 * The set of traces that constitute the experiment
107 */
108 private boolean fInitialized = false;
109
110
111 // ------------------------------------------------------------------------
112 // Construction
113 // ------------------------------------------------------------------------
114
115 /**
116 * Default constructor. Should not be used directly, but is needed for
117 * extension points.
118 *
119 * @deprecated Do not call this directly (but do not remove it either!)
120 */
121 @Deprecated
122 public TmfExperiment() {
123 super();
124 }
125
126 /**
127 * Constructor of an experiment, taking the type, path, traces,
128 * indexPageSize and resource
129 *
130 * @param type
131 * The event type
132 * @param path
133 * The experiment path
134 * @param traces
135 * The experiment set of traces
136 * @param indexPageSize
137 * The experiment index page size. You can use
138 * {@link TmfExperiment#DEFAULT_INDEX_PAGE_SIZE} for a default
139 * value.
140 * @param resource
141 * The resource associated to the experiment. You can use 'null'
142 * for no resources (tests, etc.)
143 */
144 public TmfExperiment(final Class<? extends ITmfEvent> type,
145 final String path,
146 final ITmfTrace[] traces,
147 final int indexPageSize,
148 final @Nullable IResource resource) {
149 initExperiment(type, path, traces, indexPageSize, resource);
150 }
151
152 @Override
153 protected ITmfTraceIndexer createIndexer(int interval) {
154 if (getCheckpointSize() > 0) {
155 return new TmfBTreeTraceIndexer(this, interval);
156 }
157 return super.createIndexer(interval);
158 }
159
160 /**
161 * Clears the experiment
162 */
163 @Override
164 public synchronized void dispose() {
165
166 // Clean up the index if applicable
167 if (getIndexer() != null) {
168 getIndexer().dispose();
169 }
170
171 if (fTraces != null) {
172 for (final ITmfTrace trace : fTraces) {
173 trace.dispose();
174 }
175 fTraces = null;
176 }
177 super.dispose();
178 }
179
180 // ------------------------------------------------------------------------
181 // ITmfTrace - Initializers
182 // ------------------------------------------------------------------------
183
184 @Override
185 public void initTrace(final IResource resource, final String path, final Class<? extends ITmfEvent> type) {
186 /* Do nothing for experiments */
187 }
188
189 /**
190 * Initialization of an experiment, taking the type, path, traces,
191 * indexPageSize and resource
192 *
193 * @param type
194 * the event type
195 * @param path
196 * the experiment path
197 * @param traces
198 * the experiment set of traces
199 * @param indexPageSize
200 * the experiment index page size
201 * @param resource
202 * the resource associated to the experiment
203 * @since 3.0
204 */
205 public void initExperiment(final Class<? extends ITmfEvent> type,
206 final String path,
207 final ITmfTrace[] traces,
208 final int indexPageSize,
209 final @Nullable IResource resource) {
210
211 setCacheSize(indexPageSize);
212 setStreamingInterval(0);
213 setParser(this);
214 // traces have to be set before super.initialize()
215 fTraces = traces;
216 try {
217 super.initialize(resource, path, type);
218 } catch (TmfTraceException e) {
219 Activator.logError("Error initializing experiment", e); //$NON-NLS-1$
220 }
221
222 if (resource != null) {
223 this.synchronizeTraces();
224 }
225 }
226
227 /**
228 * @since 2.0
229 */
230 @Override
231 public IStatus validate(final IProject project, final String path) {
232 return Status.OK_STATUS;
233 }
234
235 // ------------------------------------------------------------------------
236 // Accessors
237 // ------------------------------------------------------------------------
238
239 /**
240 * Get the traces contained in this experiment.
241 *
242 * @return The array of contained traces
243 */
244 public ITmfTrace[] getTraces() {
245 return fTraces;
246 }
247
248 /**
249 * Returns the timestamp of the event at the requested index. If none,
250 * returns null.
251 *
252 * @param index
253 * the event index (rank)
254 * @return the corresponding event timestamp
255 * @since 2.0
256 */
257 public ITmfTimestamp getTimestamp(final int index) {
258 final ITmfContext context = seekEvent(index);
259 final ITmfEvent event = getNext(context);
260 context.dispose();
261 return (event != null) ? event.getTimestamp() : null;
262 }
263
264 // ------------------------------------------------------------------------
265 // Request management
266 // ------------------------------------------------------------------------
267
268 /**
269 * @since 2.0
270 */
271 @Override
272 public synchronized ITmfContext armRequest(final ITmfEventRequest request) {
273
274 // Make sure we have something to read from
275 if (fTraces == null) {
276 return null;
277 }
278
279 if (!TmfTimestamp.BIG_BANG.equals(request.getRange().getStartTime())
280 && request.getIndex() == 0) {
281 final ITmfContext context = seekEvent(request.getRange().getStartTime());
282 request.setStartIndex((int) context.getRank());
283 return context;
284
285 }
286
287 return seekEvent(request.getIndex());
288 }
289
290 // ------------------------------------------------------------------------
291 // ITmfTrace trace positioning
292 // ------------------------------------------------------------------------
293
294 /**
295 * @since 3.0
296 */
297 @Override
298 public synchronized ITmfContext seekEvent(final ITmfLocation location) {
299 // Validate the location
300 if (location != null && !(location instanceof TmfExperimentLocation)) {
301 return null; // Throw an exception?
302 }
303 // Make sure we have something to read from
304 if (fTraces == null) {
305 return null;
306 }
307
308 // Initialize the location array if necessary
309 TmfLocationArray locationArray = ((location == null) ?
310 new TmfLocationArray(fTraces.length) :
311 ((TmfExperimentLocation) location).getLocationInfo());
312
313 ITmfLocation[] locations = locationArray.getLocations();
314 long[] ranks = locationArray.getRanks();
315
316 // Create and populate the context's traces contexts
317 final TmfExperimentContext context = new TmfExperimentContext(fTraces.length);
318
319 // Position the traces
320 long rank = 0;
321 for (int i = 0; i < fTraces.length; i++) {
322 // Get the relevant trace attributes
323 final ITmfContext traceContext = fTraces[i].seekEvent(locations[i]);
324 context.setContext(i, traceContext);
325 traceContext.setRank(ranks[i]);
326 // update location after seek
327 locations[i] = traceContext.getLocation();
328 context.setEvent(i, fTraces[i].getNext(traceContext));
329 rank += ranks[i];
330 }
331
332 // Finalize context
333 context.setLocation(new TmfExperimentLocation(new TmfLocationArray(locations, ranks)));
334 context.setLastTrace(TmfExperimentContext.NO_TRACE);
335 context.setRank(rank);
336
337 return context;
338 }
339
340 // ------------------------------------------------------------------------
341 // ITmfTrace - SeekEvent operations (returning a trace context)
342 // ------------------------------------------------------------------------
343
344 @Override
345 public ITmfContext seekEvent(final double ratio) {
346 final ITmfContext context = seekEvent(Math.round(ratio * getNbEvents()));
347 return context;
348 }
349
350 /**
351 * @since 3.0
352 */
353 @Override
354 public double getLocationRatio(final ITmfLocation location) {
355 if (location instanceof TmfExperimentLocation) {
356 long rank = 0;
357 TmfLocationArray locationArray = ((TmfExperimentLocation) location).getLocationInfo();
358 for (int i = 0; i < locationArray.size(); i++) {
359 rank += locationArray.getRank(i);
360 }
361 return (double) rank / getNbEvents();
362 }
363 return 0.0;
364 }
365
366 /**
367 * @since 3.0
368 */
369 @Override
370 public ITmfLocation getCurrentLocation() {
371 // never used
372 return null;
373 }
374
375 // ------------------------------------------------------------------------
376 // ITmfTrace trace positioning
377 // ------------------------------------------------------------------------
378
379 @Override
380 public synchronized ITmfEvent parseEvent(final ITmfContext context) {
381 final ITmfContext tmpContext = seekEvent(context.getLocation());
382 final ITmfEvent event = getNext(tmpContext);
383 return event;
384 }
385
386 @Override
387 public synchronized ITmfEvent getNext(ITmfContext context) {
388
389 // Validate the context
390 if (!(context instanceof TmfExperimentContext)) {
391 return null; // Throw an exception?
392 }
393
394 // Make sure that we have something to read from
395 if (fTraces == null) {
396 return null;
397 }
398
399 TmfExperimentContext expContext = (TmfExperimentContext) context;
400
401 // If an event was consumed previously, first get the next one from that
402 // trace
403 final int lastTrace = expContext.getLastTrace();
404 if (lastTrace != TmfExperimentContext.NO_TRACE) {
405 final ITmfContext traceContext = expContext.getContext(lastTrace);
406 expContext.setEvent(lastTrace, fTraces[lastTrace].getNext(traceContext));
407 expContext.setLastTrace(TmfExperimentContext.NO_TRACE);
408 }
409
410 // Scan the candidate events and identify the "next" trace to read from
411 int trace = TmfExperimentContext.NO_TRACE;
412 ITmfTimestamp timestamp = TmfTimestamp.BIG_CRUNCH;
413 for (int i = 0; i < fTraces.length; i++) {
414 final ITmfEvent event = expContext.getEvent(i);
415 if (event != null && event.getTimestamp() != null) {
416 final ITmfTimestamp otherTS = event.getTimestamp();
417 if (otherTS.compareTo(timestamp) < 0) {
418 trace = i;
419 timestamp = otherTS;
420 }
421 }
422 }
423
424 ITmfEvent event = null;
425 if (trace != TmfExperimentContext.NO_TRACE) {
426 event = expContext.getEvent(trace);
427 if (event != null) {
428 updateAttributes(expContext, event.getTimestamp());
429 expContext.increaseRank();
430 expContext.setLastTrace(trace);
431 final ITmfContext traceContext = expContext.getContext(trace);
432 if (traceContext == null) {
433 throw new IllegalStateException();
434 }
435
436 // Update the experiment location
437 TmfLocationArray locationArray = new TmfLocationArray(
438 ((TmfExperimentLocation) expContext.getLocation()).getLocationInfo(),
439 trace, traceContext.getLocation(), traceContext.getRank());
440 expContext.setLocation(new TmfExperimentLocation(locationArray));
441
442 processEvent(event);
443 }
444 }
445
446 return event;
447 }
448
449 /**
450 * @since 2.0
451 */
452 @Override
453 public ITmfTimestamp getInitialRangeOffset() {
454 if ((fTraces == null) || (fTraces.length == 0)) {
455 return super.getInitialRangeOffset();
456 }
457
458 ITmfTimestamp initTs = TmfTimestamp.BIG_CRUNCH;
459 for (int i = 0; i < fTraces.length; i++) {
460 ITmfTimestamp ts = fTraces[i].getInitialRangeOffset();
461 if (ts.compareTo(initTs) < 0) {
462 initTs = ts;
463 }
464 }
465 return initTs;
466 }
467
468 /**
469 * Get the path to the folder in the supplementary file where
470 * synchronization-related data can be kept so they are not deleted when the
471 * experiment is synchronized. Analysis involved in synchronization can put
472 * their supplementary files in there so they are preserved after
473 * synchronization.
474 *
475 * If the directory does not exist, it will be created. A return value of
476 * <code>null</code> means either the trace resource does not exist or
477 * supplementary resources cannot be kept.
478 *
479 * @param absolute
480 * If <code>true</code>, it returns the absolute path in the file
481 * system, including the supplementary file path. Otherwise, it
482 * returns only the directory name.
483 * @return The path to the folder where synchronization-related
484 * supplementary files can be kept or <code>null</code> if not
485 * available.
486 * @since 3.2
487 */
488 public String getSynchronizationFolder(boolean absolute) {
489 /* Set up the path to the synchronization file we'll use */
490 IResource resource = this.getResource();
491 String syncDirectory = null;
492
493 try {
494 /* get the directory where the file will be stored. */
495 if (resource != null) {
496 String fullDirectory = resource.getPersistentProperty(TmfCommonConstants.TRACE_SUPPLEMENTARY_FOLDER);
497 /* Create the synchronization data directory if not present */
498 if (fullDirectory != null) {
499 fullDirectory = fullDirectory + File.separator + SYNCHRONIZATION_DIRECTORY;
500 File syncDir = new File(fullDirectory);
501 syncDir.mkdirs();
502 }
503 if (absolute) {
504 syncDirectory = fullDirectory;
505 } else {
506 syncDirectory = SYNCHRONIZATION_DIRECTORY;
507 }
508 }
509 } catch (CoreException e) {
510 return null;
511 }
512
513 return syncDirectory;
514 }
515
516 /**
517 * Synchronizes the traces of an experiment. By default it only tries to
518 * read a synchronization file if it exists
519 *
520 * @return The synchronization object
521 * @since 3.0
522 */
523 public synchronized SynchronizationAlgorithm synchronizeTraces() {
524 return synchronizeTraces(false);
525 }
526
527 /**
528 * Synchronizes the traces of an experiment.
529 *
530 * @param doSync
531 * Whether to actually synchronize or just try opening a sync
532 * file
533 * @return The synchronization object
534 * @since 3.0
535 */
536 public synchronized SynchronizationAlgorithm synchronizeTraces(boolean doSync) {
537
538 String syncDirectory = getSynchronizationFolder(true);
539
540 final File syncFile = (syncDirectory != null) ? new File(syncDirectory + File.separator + SYNCHRONIZATION_FILE_NAME) : null;
541
542 final SynchronizationAlgorithm syncAlgo = SynchronizationManager.synchronizeTraces(syncFile, Arrays.asList(fTraces), doSync);
543
544 final TmfTraceSynchronizedSignal signal = new TmfTraceSynchronizedSignal(this, syncAlgo);
545
546 /* Broadcast in separate thread to prevent deadlock */
547 new Thread() {
548 @Override
549 public void run() {
550 broadcast(signal);
551 }
552 }.start();
553
554 return syncAlgo;
555 }
556
557 @Override
558 @SuppressWarnings("nls")
559 public synchronized String toString() {
560 return "[TmfExperiment (" + getName() + ")]";
561 }
562
563 // ------------------------------------------------------------------------
564 // Streaming support
565 // ------------------------------------------------------------------------
566
567 private synchronized void initializeStreamingMonitor() {
568
569 if (fInitialized) {
570 return;
571 }
572 fInitialized = true;
573
574 if (getStreamingInterval() == 0) {
575 final ITmfContext context = seekEvent(0);
576 final ITmfEvent event = getNext(context);
577 context.dispose();
578 if (event == null) {
579 return;
580 }
581 final TmfTimeRange timeRange = new TmfTimeRange(event.getTimestamp(), TmfTimestamp.BIG_CRUNCH);
582 final TmfTraceRangeUpdatedSignal signal = new TmfTraceRangeUpdatedSignal(this, this, timeRange);
583
584 // Broadcast in separate thread to prevent deadlock
585 new Thread() {
586 @Override
587 public void run() {
588 broadcast(signal);
589 }
590 }.start();
591 return;
592 }
593
594 final Thread thread = new Thread("Streaming Monitor for experiment " + getName()) { //$NON-NLS-1$
595 private ITmfTimestamp safeTimestamp = null;
596 private ITmfTimestamp lastSafeTimestamp = null;
597 private TmfTimeRange timeRange = null;
598
599 @Override
600 public void run() {
601 while (!executorIsShutdown()) {
602 if (!getIndexer().isIndexing()) {
603 ITmfTimestamp startTimestamp = TmfTimestamp.BIG_CRUNCH;
604 ITmfTimestamp endTimestamp = TmfTimestamp.BIG_BANG;
605 for (final ITmfTrace trace : fTraces) {
606 if (trace.getStartTime().compareTo(startTimestamp) < 0) {
607 startTimestamp = trace.getStartTime();
608 }
609 if (trace.getStreamingInterval() != 0 && trace.getEndTime().compareTo(endTimestamp) > 0) {
610 endTimestamp = trace.getEndTime();
611 }
612 }
613 if (safeTimestamp != null && (lastSafeTimestamp == null || safeTimestamp.compareTo(lastSafeTimestamp) > 0)) {
614 timeRange = new TmfTimeRange(startTimestamp, safeTimestamp);
615 lastSafeTimestamp = safeTimestamp;
616 } else {
617 timeRange = null;
618 }
619 safeTimestamp = endTimestamp;
620 if (timeRange != null) {
621 final TmfTraceRangeUpdatedSignal signal =
622 new TmfTraceRangeUpdatedSignal(TmfExperiment.this, TmfExperiment.this, timeRange);
623 broadcast(signal);
624 }
625 }
626 try {
627 Thread.sleep(getStreamingInterval());
628 } catch (final InterruptedException e) {
629 e.printStackTrace();
630 }
631 }
632 }
633 };
634 thread.start();
635 }
636
637 @Override
638 public long getStreamingInterval() {
639 long interval = 0;
640 for (final ITmfTrace trace : fTraces) {
641 interval = Math.max(interval, trace.getStreamingInterval());
642 }
643 return interval;
644 }
645
646 // ------------------------------------------------------------------------
647 // Signal handlers
648 // ------------------------------------------------------------------------
649
650 @Override
651 @TmfSignalHandler
652 public void traceOpened(TmfTraceOpenedSignal signal) {
653 if (signal.getTrace() == this) {
654 initializeStreamingMonitor();
655
656 /* Initialize the analysis */
657 MultiStatus status = new MultiStatus(Activator.PLUGIN_ID, IStatus.OK, null, null);
658 status.add(executeAnalysis());
659 if (!status.isOK()) {
660 Activator.log(status);
661 }
662 TmfTraceManager.refreshSupplementaryFiles(this);
663 }
664 }
665
666 /**
667 * @since 3.0
668 */
669 @Override
670 public synchronized int getCheckpointSize() {
671 int totalCheckpointSize = 0;
672 try {
673 if (fTraces != null) {
674 for (final ITmfTrace trace : fTraces) {
675 if (!(trace instanceof ITmfPersistentlyIndexable)) {
676 return 0;
677 }
678
679 ITmfPersistentlyIndexable persistableIndexTrace = (ITmfPersistentlyIndexable) trace;
680 int currentTraceCheckpointSize = persistableIndexTrace.getCheckpointSize();
681 if (currentTraceCheckpointSize <= 0) {
682 return 0;
683 }
684 totalCheckpointSize += currentTraceCheckpointSize;
685 // each entry in the TmfLocationArray has a rank in addition
686 // of the location
687 totalCheckpointSize += 8;
688 }
689 }
690 } catch (UnsupportedOperationException e) {
691 return 0;
692 }
693
694 return totalCheckpointSize;
695 }
696
697 /**
698 * @since 3.0
699 */
700 @Override
701 public ITmfLocation restoreLocation(ByteBuffer bufferIn) {
702 ITmfLocation[] locations = new ITmfLocation[fTraces.length];
703 long[] ranks = new long[fTraces.length];
704 for (int i = 0; i < fTraces.length; ++i) {
705 final ITmfTrace trace = fTraces[i];
706 locations[i] = ((ITmfPersistentlyIndexable) trace).restoreLocation(bufferIn);
707 ranks[i] = bufferIn.getLong();
708 }
709 TmfLocationArray arr = new TmfLocationArray(locations, ranks);
710 TmfExperimentLocation l = new TmfExperimentLocation(arr);
711 return l;
712 }
713
714 }
This page took 0.089582 seconds and 5 git commands to generate.